[SIEM][Lists] Adds _find to value lists
## Summary Adds the REST and API routes for find and filter for exception lists and value lists * Fixes bugs with string parameters for the _find with exception lists * Adds the _find for the value based lists * More scripts for how to filter things for both list values and exception lists * Misc type script fixes * Adds a cursor to move from the previous page to the next page * Adds name space 'agnostic' vs. 'single' feature for exception_lists **REST API's:** ```ts POST /api/lists/_find POST /api/lists/items/_find POST /api/exception_lists/_find POST /api/exception_lists/items/_find ``` **Parameters you can send:** * sort * sort_order * filter * page * per_page * list_id (for list items only and required) * cursor (for finding the next page or advancing to deep pages) **See test scripts below:** ```sh find_exception_list_items_by_filter.sh find_exception_lists_by_filter.sh find_list_items.sh find_list_items_with_cursor.sh find_list_items_with_sort.sh find_list_items_with_sort_cursor.sh find_lists.sh find_lists_with_cursor.sh find_lists_with_filter.sh find_lists_with_sort.sh find_lists_with_sort_cursor.sh ``` ### Checklist Note: Unit tests are left out as this is blocking people but I will be adding tests as this is being reviewed unless someone needs these features now. This is still all behind a feature flag and considered to be in the area of proof of concept and not production ready until more tests and end to tests are added. - [ ] [Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios
This commit is contained in:
parent
6643b9c191
commit
19fe3461f4
109 changed files with 1921 additions and 196 deletions
|
@ -28,3 +28,4 @@ export const META = {};
|
|||
export const TYPE = 'ip';
|
||||
export const VALUE = '127.0.0.1';
|
||||
export const VALUE_2 = '255.255.255';
|
||||
export const NAMESPACE_TYPE = 'single';
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
import * as t from 'io-ts';
|
||||
|
||||
import { DefaultStringArray, NonEmptyString } from '../types';
|
||||
import { DefaultNamespace } from '../types/default_namespace';
|
||||
|
||||
export const name = t.string;
|
||||
export type Name = t.TypeOf<typeof name>;
|
||||
|
@ -97,8 +98,38 @@ export const itemIdOrUndefined = t.union([item_id, t.undefined]);
|
|||
export type ItemIdOrUndefined = t.TypeOf<typeof itemIdOrUndefined>;
|
||||
|
||||
export const per_page = t.number; // TODO: Change this out for PositiveNumber from siem
|
||||
export type PerPage = t.TypeOf<typeof per_page>;
|
||||
|
||||
export const perPageOrUndefined = t.union([per_page, t.undefined]);
|
||||
export type PerPageOrUndefined = t.TypeOf<typeof perPageOrUndefined>;
|
||||
|
||||
export const total = t.number; // TODO: Change this out for PositiveNumber from siem
|
||||
export const totalUndefined = t.union([total, t.undefined]);
|
||||
export type TotalOrUndefined = t.TypeOf<typeof totalUndefined>;
|
||||
|
||||
export const page = t.number; // TODO: Change this out for PositiveNumber from siem
|
||||
export type Page = t.TypeOf<typeof page>;
|
||||
|
||||
export const pageOrUndefined = t.union([page, t.undefined]);
|
||||
export type PageOrUndefined = t.TypeOf<typeof pageOrUndefined>;
|
||||
|
||||
export const sort_field = t.string;
|
||||
export const sortFieldOrUndefined = t.union([sort_field, t.undefined]);
|
||||
export type SortFieldOrUndefined = t.TypeOf<typeof sortFieldOrUndefined>;
|
||||
|
||||
export const sort_order = t.keyof({ asc: null, desc: null });
|
||||
export const sortOrderOrUndefined = t.union([sort_order, t.undefined]);
|
||||
export type SortOrderOrUndefined = t.TypeOf<typeof sortOrderOrUndefined>;
|
||||
|
||||
export const filter = t.string;
|
||||
export type Filter = t.TypeOf<typeof filter>;
|
||||
export const filterOrUndefined = t.union([filter, t.undefined]);
|
||||
export type FilterOrUndefined = t.TypeOf<typeof filterOrUndefined>;
|
||||
|
||||
export const cursor = t.string;
|
||||
export type Cursor = t.TypeOf<typeof cursor>;
|
||||
export const cursorOrUndefined = t.union([cursor, t.undefined]);
|
||||
export type CursorOrUndefined = t.TypeOf<typeof cursorOrUndefined>;
|
||||
|
||||
export const namespace_type = DefaultNamespace;
|
||||
export type NamespaceType = t.TypeOf<typeof namespace_type>;
|
||||
|
|
|
@ -10,6 +10,7 @@ import * as t from 'io-ts';
|
|||
|
||||
import {
|
||||
ItemId,
|
||||
NamespaceType,
|
||||
Tags,
|
||||
_Tags,
|
||||
_tags,
|
||||
|
@ -19,6 +20,7 @@ import {
|
|||
list_id,
|
||||
meta,
|
||||
name,
|
||||
namespace_type,
|
||||
tags,
|
||||
} from '../common/schemas';
|
||||
import { Identity, RequiredKeepUndefined } from '../../types';
|
||||
|
@ -41,6 +43,7 @@ export const createExceptionListItemSchema = t.intersection([
|
|||
entries: DefaultEntryArray, // defaults to empty array if not set during decode
|
||||
item_id: DefaultUuid, // defaults to GUID (uuid v4) if not set during decode
|
||||
meta, // defaults to undefined if not set during decode
|
||||
namespace_type, // defaults to 'single' if not set during decode
|
||||
tags, // defaults to empty array if not set during decode
|
||||
})
|
||||
),
|
||||
|
@ -53,13 +56,16 @@ export type CreateExceptionListItemSchema = RequiredKeepUndefined<
|
|||
t.TypeOf<typeof createExceptionListItemSchema>
|
||||
>;
|
||||
|
||||
// This type is used after a decode since the arrays turn into defaults of empty arrays
|
||||
// and if a item_id is not specified it turns into a default GUID
|
||||
// This type is used after a decode since some things are defaults after a decode.
|
||||
export type CreateExceptionListItemSchemaDecoded = Identity<
|
||||
Omit<CreateExceptionListItemSchema, '_tags' | 'tags' | 'item_id' | 'entries'> & {
|
||||
Omit<
|
||||
CreateExceptionListItemSchema,
|
||||
'_tags' | 'tags' | 'item_id' | 'entries' | 'namespace_type'
|
||||
> & {
|
||||
_tags: _Tags;
|
||||
tags: Tags;
|
||||
item_id: ItemId;
|
||||
entries: EntriesArray;
|
||||
namespace_type: NamespaceType;
|
||||
}
|
||||
>;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { DESCRIPTION, LIST_ID, META, NAME, TYPE } from '../../constants.mock';
|
||||
import { DESCRIPTION, LIST_ID, META, NAME, NAMESPACE_TYPE, TYPE } from '../../constants.mock';
|
||||
|
||||
import { CreateExceptionListSchema } from './create_exception_list_schema';
|
||||
|
||||
|
@ -14,6 +14,7 @@ export const getCreateExceptionListSchemaMock = (): CreateExceptionListSchema =>
|
|||
list_id: LIST_ID,
|
||||
meta: META,
|
||||
name: NAME,
|
||||
namespace_type: NAMESPACE_TYPE,
|
||||
tags: [],
|
||||
type: TYPE,
|
||||
});
|
||||
|
|
|
@ -10,6 +10,7 @@ import * as t from 'io-ts';
|
|||
|
||||
import {
|
||||
ListId,
|
||||
NamespaceType,
|
||||
Tags,
|
||||
_Tags,
|
||||
_tags,
|
||||
|
@ -17,6 +18,7 @@ import {
|
|||
exceptionListType,
|
||||
meta,
|
||||
name,
|
||||
namespace_type,
|
||||
tags,
|
||||
} from '../common/schemas';
|
||||
import { Identity, RequiredKeepUndefined } from '../../types';
|
||||
|
@ -35,6 +37,7 @@ export const createExceptionListSchema = t.intersection([
|
|||
_tags, // defaults to empty array if not set during decode
|
||||
list_id: DefaultUuid, // defaults to a GUID (UUID v4) string if not set during decode
|
||||
meta, // defaults to undefined if not set during decode
|
||||
namespace_type, // defaults to 'single' if not set during decode
|
||||
tags, // defaults to empty array if not set during decode
|
||||
})
|
||||
),
|
||||
|
@ -45,11 +48,12 @@ export type CreateExceptionListSchema = RequiredKeepUndefined<
|
|||
t.TypeOf<typeof createExceptionListSchema>
|
||||
>;
|
||||
|
||||
// This type is used after a decode since the arrays turn into defaults of empty arrays.
|
||||
// This type is used after a decode since some things are defaults after a decode.
|
||||
export type CreateExceptionListSchemaDecoded = Identity<
|
||||
CreateExceptionListSchema & {
|
||||
Omit<CreateExceptionListSchema, '_tags' | 'tags' | 'list_id' | 'namespace_type'> & {
|
||||
_tags: _Tags;
|
||||
tags: Tags;
|
||||
list_id: ListId;
|
||||
namespace_type: NamespaceType;
|
||||
}
|
||||
>;
|
||||
|
|
|
@ -8,13 +8,22 @@
|
|||
|
||||
import * as t from 'io-ts';
|
||||
|
||||
import { id, item_id } from '../common/schemas';
|
||||
import { NamespaceType, id, item_id, namespace_type } from '../common/schemas';
|
||||
|
||||
export const deleteExceptionListItemSchema = t.exact(
|
||||
t.partial({
|
||||
id,
|
||||
item_id,
|
||||
namespace_type, // defaults to 'single' if not set during decode
|
||||
})
|
||||
);
|
||||
|
||||
export type DeleteExceptionListItemSchema = t.TypeOf<typeof deleteExceptionListItemSchema>;
|
||||
|
||||
// This type is used after a decode since some things are defaults after a decode.
|
||||
export type DeleteExceptionListItemSchemaDecoded = Omit<
|
||||
DeleteExceptionListItemSchema,
|
||||
'namespace_type'
|
||||
> & {
|
||||
namespace_type: NamespaceType;
|
||||
};
|
||||
|
|
|
@ -8,13 +8,19 @@
|
|||
|
||||
import * as t from 'io-ts';
|
||||
|
||||
import { id, list_id } from '../common/schemas';
|
||||
import { NamespaceType, id, list_id, namespace_type } from '../common/schemas';
|
||||
|
||||
export const deleteExceptionListSchema = t.exact(
|
||||
t.partial({
|
||||
id,
|
||||
list_id,
|
||||
namespace_type, // defaults to 'single' if not set during decode
|
||||
})
|
||||
);
|
||||
|
||||
export type DeleteExceptionListSchema = t.TypeOf<typeof deleteExceptionListSchema>;
|
||||
|
||||
// This type is used after a decode since some things are defaults after a decode.
|
||||
export type DeleteExceptionListSchemaDecoded = Omit<DeleteExceptionListSchema, 'namespace_type'> & {
|
||||
namespace_type: NamespaceType;
|
||||
};
|
||||
|
|
|
@ -8,8 +8,16 @@
|
|||
|
||||
import * as t from 'io-ts';
|
||||
|
||||
import { filter, list_id, page, per_page, sort_field, sort_order } from '../common/schemas';
|
||||
import {
|
||||
NamespaceType,
|
||||
filter,
|
||||
list_id,
|
||||
namespace_type,
|
||||
sort_field,
|
||||
sort_order,
|
||||
} from '../common/schemas';
|
||||
import { RequiredKeepUndefined } from '../../types';
|
||||
import { StringToPositiveNumber } from '../types/string_to_positive_number';
|
||||
|
||||
export const findExceptionListItemSchema = t.intersection([
|
||||
t.exact(
|
||||
|
@ -20,8 +28,9 @@ export const findExceptionListItemSchema = t.intersection([
|
|||
t.exact(
|
||||
t.partial({
|
||||
filter, // defaults to undefined if not set during decode
|
||||
page, // defaults to undefined if not set during decode
|
||||
per_page, // defaults to undefined if not set during decode
|
||||
namespace_type, // defaults to 'single' if not set during decode
|
||||
page: StringToPositiveNumber, // defaults to undefined if not set during decode
|
||||
per_page: StringToPositiveNumber, // defaults to undefined if not set during decode
|
||||
sort_field, // defaults to undefined if not set during decode
|
||||
sort_order, // defaults to undefined if not set during decode
|
||||
})
|
||||
|
@ -30,6 +39,19 @@ export const findExceptionListItemSchema = t.intersection([
|
|||
|
||||
export type FindExceptionListItemSchemaPartial = t.TypeOf<typeof findExceptionListItemSchema>;
|
||||
|
||||
// This type is used after a decode since some things are defaults after a decode.
|
||||
export type FindExceptionListItemSchemaPartialDecoded = Omit<
|
||||
FindExceptionListItemSchemaPartial,
|
||||
'namespace_type'
|
||||
> & {
|
||||
namespace_type: NamespaceType;
|
||||
};
|
||||
|
||||
// This type is used after a decode since some things are defaults after a decode.
|
||||
export type FindExceptionListItemSchemaDecoded = RequiredKeepUndefined<
|
||||
FindExceptionListItemSchemaPartialDecoded
|
||||
>;
|
||||
|
||||
export type FindExceptionListItemSchema = RequiredKeepUndefined<
|
||||
t.TypeOf<typeof findExceptionListItemSchema>
|
||||
>;
|
||||
|
|
|
@ -8,14 +8,16 @@
|
|||
|
||||
import * as t from 'io-ts';
|
||||
|
||||
import { filter, page, per_page, sort_field, sort_order } from '../common/schemas';
|
||||
import { NamespaceType, filter, namespace_type, sort_field, sort_order } from '../common/schemas';
|
||||
import { RequiredKeepUndefined } from '../../types';
|
||||
import { StringToPositiveNumber } from '../types/string_to_positive_number';
|
||||
|
||||
export const findExceptionListSchema = t.exact(
|
||||
t.partial({
|
||||
filter, // defaults to undefined if not set during decode
|
||||
page, // defaults to undefined if not set during decode
|
||||
per_page, // defaults to undefined if not set during decode
|
||||
namespace_type, // defaults to 'single' if not set during decode
|
||||
page: StringToPositiveNumber, // defaults to undefined if not set during decode
|
||||
per_page: StringToPositiveNumber, // defaults to undefined if not set during decode
|
||||
sort_field, // defaults to undefined if not set during decode
|
||||
sort_order, // defaults to undefined if not set during decode
|
||||
})
|
||||
|
@ -23,6 +25,19 @@ export const findExceptionListSchema = t.exact(
|
|||
|
||||
export type FindExceptionListSchemaPartial = t.TypeOf<typeof findExceptionListSchema>;
|
||||
|
||||
// This type is used after a decode since some things are defaults after a decode.
|
||||
export type FindExceptionListSchemaPartialDecoded = Omit<
|
||||
FindExceptionListSchemaPartial,
|
||||
'namespace_type'
|
||||
> & {
|
||||
namespace_type: NamespaceType;
|
||||
};
|
||||
|
||||
// This type is used after a decode since some things are defaults after a decode.
|
||||
export type FindExceptionListSchemaDecoded = RequiredKeepUndefined<
|
||||
FindExceptionListSchemaPartialDecoded
|
||||
>;
|
||||
|
||||
export type FindExceptionListSchema = RequiredKeepUndefined<
|
||||
t.TypeOf<typeof findExceptionListSchema>
|
||||
>;
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
|
||||
import * as t from 'io-ts';
|
||||
|
||||
import { cursor, filter, list_id, sort_field, sort_order } from '../common/schemas';
|
||||
import { Identity, RequiredKeepUndefined } from '../../types';
|
||||
import { StringToPositiveNumber } from '../types/string_to_positive_number';
|
||||
|
||||
export const findListItemSchema = t.intersection([
|
||||
t.exact(t.type({ list_id })),
|
||||
t.exact(
|
||||
t.partial({
|
||||
cursor, // defaults to undefined if not set during decode
|
||||
filter, // defaults to undefined if not set during decode
|
||||
page: StringToPositiveNumber, // defaults to undefined if not set during decode
|
||||
per_page: StringToPositiveNumber, // defaults to undefined if not set during decode
|
||||
sort_field, // defaults to undefined if not set during decode
|
||||
sort_order, // defaults to undefined if not set during decode
|
||||
})
|
||||
),
|
||||
]);
|
||||
|
||||
export type FindListItemSchemaPartial = Identity<t.TypeOf<typeof findListItemSchema>>;
|
||||
|
||||
export type FindListItemSchema = RequiredKeepUndefined<t.TypeOf<typeof findListItemSchema>>;
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
|
||||
import * as t from 'io-ts';
|
||||
|
||||
import { cursor, filter, sort_field, sort_order } from '../common/schemas';
|
||||
import { RequiredKeepUndefined } from '../../types';
|
||||
import { StringToPositiveNumber } from '../types/string_to_positive_number';
|
||||
|
||||
export const findListSchema = t.exact(
|
||||
t.partial({
|
||||
cursor, // defaults to undefined if not set during decode
|
||||
filter, // defaults to undefined if not set during decode
|
||||
page: StringToPositiveNumber, // defaults to undefined if not set during decode
|
||||
per_page: StringToPositiveNumber, // defaults to undefined if not set during decode
|
||||
sort_field, // defaults to undefined if not set during decode
|
||||
sort_order, // defaults to undefined if not set during decode
|
||||
})
|
||||
);
|
||||
|
||||
export type FindListSchemaPartial = t.TypeOf<typeof findListSchema>;
|
||||
|
||||
export type FindListSchema = RequiredKeepUndefined<t.TypeOf<typeof findListSchema>>;
|
|
@ -15,6 +15,8 @@ export * from './delete_list_schema';
|
|||
export * from './export_list_item_query_schema';
|
||||
export * from './find_exception_list_item_schema';
|
||||
export * from './find_exception_list_schema';
|
||||
export * from './find_list_item_schema';
|
||||
export * from './find_list_schema';
|
||||
export * from './import_list_item_schema';
|
||||
export * from './patch_list_item_schema';
|
||||
export * from './patch_list_schema';
|
||||
|
|
|
@ -8,13 +8,28 @@
|
|||
|
||||
import * as t from 'io-ts';
|
||||
|
||||
import { id, item_id } from '../common/schemas';
|
||||
import { NamespaceType, id, item_id, namespace_type } from '../common/schemas';
|
||||
import { RequiredKeepUndefined } from '../../types';
|
||||
|
||||
export const readExceptionListItemSchema = t.partial({
|
||||
id,
|
||||
item_id,
|
||||
namespace_type, // defaults to 'single' if not set during decode
|
||||
});
|
||||
|
||||
export type ReadExceptionListItemSchemaPartial = t.TypeOf<typeof readExceptionListItemSchema>;
|
||||
|
||||
// This type is used after a decode since some things are defaults after a decode.
|
||||
export type ReadExceptionListItemSchemaPartialDecoded = Omit<
|
||||
ReadExceptionListItemSchemaPartial,
|
||||
'namespace_type'
|
||||
> & {
|
||||
namespace_type: NamespaceType;
|
||||
};
|
||||
|
||||
// This type is used after a decode since some things are defaults after a decode.
|
||||
export type ReadExceptionListItemSchemaDecoded = RequiredKeepUndefined<
|
||||
ReadExceptionListItemSchemaPartialDecoded
|
||||
>;
|
||||
|
||||
export type ReadExceptionListItemSchema = RequiredKeepUndefined<ReadExceptionListItemSchemaPartial>;
|
||||
|
|
|
@ -8,13 +8,28 @@
|
|||
|
||||
import * as t from 'io-ts';
|
||||
|
||||
import { id, list_id } from '../common/schemas';
|
||||
import { NamespaceType, id, list_id, namespace_type } from '../common/schemas';
|
||||
import { RequiredKeepUndefined } from '../../types';
|
||||
|
||||
export const readExceptionListSchema = t.partial({
|
||||
id,
|
||||
list_id,
|
||||
namespace_type, // defaults to 'single' if not set during decode
|
||||
});
|
||||
|
||||
export type ReadExceptionListSchemaPartial = t.TypeOf<typeof readExceptionListSchema>;
|
||||
|
||||
// This type is used after a decode since some things are defaults after a decode.
|
||||
export type ReadExceptionListSchemaPartialDecoded = Omit<
|
||||
ReadExceptionListSchemaPartial,
|
||||
'namespace_type'
|
||||
> & {
|
||||
namespace_type: NamespaceType;
|
||||
};
|
||||
|
||||
// This type is used after a decode since some things are defaults after a decode.
|
||||
export type ReadExceptionListSchemaDecoded = RequiredKeepUndefined<
|
||||
ReadExceptionListSchemaPartialDecoded
|
||||
>;
|
||||
|
||||
export type ReadExceptionListSchema = RequiredKeepUndefined<ReadExceptionListSchemaPartial>;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
import * as t from 'io-ts';
|
||||
|
||||
import {
|
||||
NamespaceType,
|
||||
Tags,
|
||||
_Tags,
|
||||
_tags,
|
||||
|
@ -18,6 +19,7 @@ import {
|
|||
id,
|
||||
meta,
|
||||
name,
|
||||
namespace_type,
|
||||
tags,
|
||||
} from '../common/schemas';
|
||||
import { Identity, RequiredKeepUndefined } from '../../types';
|
||||
|
@ -40,6 +42,7 @@ export const updateExceptionListItemSchema = t.intersection([
|
|||
id, // defaults to undefined if not set during decode
|
||||
item_id: t.union([t.string, t.undefined]),
|
||||
meta, // defaults to undefined if not set during decode
|
||||
namespace_type, // defaults to 'single' if not set during decode
|
||||
tags, // defaults to empty array if not set during decode
|
||||
})
|
||||
),
|
||||
|
@ -52,12 +55,12 @@ export type UpdateExceptionListItemSchema = RequiredKeepUndefined<
|
|||
t.TypeOf<typeof updateExceptionListItemSchema>
|
||||
>;
|
||||
|
||||
// This type is used after a decode since the arrays turn into defaults of empty arrays
|
||||
// and if a item_id is not specified it turns into a default GUID
|
||||
// This type is used after a decode since some things are defaults after a decode.
|
||||
export type UpdateExceptionListItemSchemaDecoded = Identity<
|
||||
Omit<UpdateExceptionListItemSchema, '_tags' | 'tags' | 'entries'> & {
|
||||
Omit<UpdateExceptionListItemSchema, '_tags' | 'tags' | 'entries' | 'namespace_type'> & {
|
||||
_tags: _Tags;
|
||||
tags: Tags;
|
||||
entries: EntriesArray;
|
||||
namespace_type: NamespaceType;
|
||||
}
|
||||
>;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
import * as t from 'io-ts';
|
||||
|
||||
import {
|
||||
NamespaceType,
|
||||
Tags,
|
||||
_Tags,
|
||||
_tags,
|
||||
|
@ -16,6 +17,7 @@ import {
|
|||
exceptionListType,
|
||||
meta,
|
||||
name,
|
||||
namespace_type,
|
||||
tags,
|
||||
} from '../common/schemas';
|
||||
import { Identity, RequiredKeepUndefined } from '../../types';
|
||||
|
@ -34,6 +36,7 @@ export const updateExceptionListSchema = t.intersection([
|
|||
id: t.union([t.string, t.undefined]), // defaults to undefined if not set during decode
|
||||
list_id: t.union([t.string, t.undefined]), // defaults to undefined if not set during decode
|
||||
meta, // defaults to undefined if not set during decode
|
||||
namespace_type, // defaults to 'single' if not set during decode
|
||||
tags, // defaults to empty array if not set during decode
|
||||
})
|
||||
),
|
||||
|
@ -46,8 +49,9 @@ export type UpdateExceptionListSchema = RequiredKeepUndefined<
|
|||
|
||||
// This type is used after a decode since the arrays turn into defaults of empty arrays.
|
||||
export type UpdateExceptionListSchemaDecoded = Identity<
|
||||
Omit<UpdateExceptionListSchema, '_tags | tags'> & {
|
||||
Omit<UpdateExceptionListSchema, '_tags | tags | namespace_type'> & {
|
||||
_tags: _Tags;
|
||||
tags: Tags;
|
||||
namespace_type: NamespaceType;
|
||||
}
|
||||
>;
|
||||
|
|
|
@ -20,6 +20,7 @@ import {
|
|||
list_id,
|
||||
metaOrUndefined,
|
||||
name,
|
||||
namespace_type,
|
||||
tags,
|
||||
tie_breaker_id,
|
||||
updated_at,
|
||||
|
@ -41,6 +42,7 @@ export const exceptionListItemSchema = t.exact(
|
|||
list_id,
|
||||
meta: metaOrUndefined,
|
||||
name,
|
||||
namespace_type,
|
||||
tags,
|
||||
tie_breaker_id,
|
||||
type: exceptionListItemType,
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
list_id,
|
||||
metaOrUndefined,
|
||||
name,
|
||||
namespace_type,
|
||||
tags,
|
||||
tie_breaker_id,
|
||||
updated_at,
|
||||
|
@ -35,6 +36,7 @@ export const exceptionListSchema = t.exact(
|
|||
list_id,
|
||||
meta: metaOrUndefined,
|
||||
name,
|
||||
namespace_type,
|
||||
tags,
|
||||
tie_breaker_id,
|
||||
type: exceptionListType,
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
|
||||
import * as t from 'io-ts';
|
||||
|
||||
import { cursor, page, per_page, total } from '../common/schemas';
|
||||
|
||||
import { listItemSchema } from './list_item_schema';
|
||||
|
||||
export const foundListItemSchema = t.exact(
|
||||
t.type({
|
||||
cursor,
|
||||
data: t.array(listItemSchema),
|
||||
page,
|
||||
per_page,
|
||||
total,
|
||||
})
|
||||
);
|
||||
|
||||
export type FoundListItemSchema = t.TypeOf<typeof foundListItemSchema>;
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
|
||||
import * as t from 'io-ts';
|
||||
|
||||
import { cursor, page, per_page, total } from '../common/schemas';
|
||||
|
||||
import { listSchema } from './list_schema';
|
||||
|
||||
export const foundListSchema = t.exact(
|
||||
t.type({
|
||||
cursor,
|
||||
data: t.array(listSchema),
|
||||
page,
|
||||
per_page,
|
||||
total,
|
||||
})
|
||||
);
|
||||
|
||||
export type FoundListSchema = t.TypeOf<typeof foundListSchema>;
|
|
@ -4,11 +4,13 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export * from './list_item_schema';
|
||||
export * from './list_schema';
|
||||
export * from './acknowledge_schema';
|
||||
export * from './list_item_index_exist_schema';
|
||||
export * from './exception_list_schema';
|
||||
export * from './exception_list_item_schema';
|
||||
export * from './found_exception_list_item_schema';
|
||||
export * from './found_exception_list_schema';
|
||||
export * from './exception_list_item_schema';
|
||||
export * from './found_list_item_schema';
|
||||
export * from './found_list_schema';
|
||||
export * from './list_item_schema';
|
||||
export * from './list_schema';
|
||||
export * from './list_item_index_exist_schema';
|
||||
|
|
|
@ -37,3 +37,6 @@ export const listSchema = t.exact(
|
|||
);
|
||||
|
||||
export type ListSchema = t.TypeOf<typeof listSchema>;
|
||||
|
||||
export const listArraySchema = t.array(listSchema);
|
||||
export type ListArraySchema = t.TypeOf<typeof listArraySchema>;
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import * as t from 'io-ts';
|
||||
import { Either } from 'fp-ts/lib/Either';
|
||||
|
||||
const namespaceType = t.keyof({ agnostic: null, single: null });
|
||||
|
||||
type NamespaceType = t.TypeOf<typeof namespaceType>;
|
||||
|
||||
export type DefaultNamespaceC = t.Type<NamespaceType, NamespaceType, unknown>;
|
||||
|
||||
/**
|
||||
* Types the DefaultNamespace as:
|
||||
* - If null or undefined, then a default string/enumeration of "single" will be used.
|
||||
*/
|
||||
export const DefaultNamespace: DefaultNamespaceC = new t.Type<
|
||||
NamespaceType,
|
||||
NamespaceType,
|
||||
unknown
|
||||
>(
|
||||
'DefaultNamespace',
|
||||
namespaceType.is,
|
||||
(input): Either<t.Errors, NamespaceType> =>
|
||||
input == null ? t.success('single') : namespaceType.decode(input),
|
||||
t.identity
|
||||
);
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import * as t from 'io-ts';
|
||||
import { Either, either } from 'fp-ts/lib/Either';
|
||||
|
||||
export type StringToPositiveNumberC = t.Type<number, string, unknown>;
|
||||
|
||||
/**
|
||||
* Types the StrongToPositiveNumber as:
|
||||
* - If a string this converts the string into a number
|
||||
* - Ensures it is a number (and not NaN)
|
||||
* - Ensures it is positive number
|
||||
*/
|
||||
export const StringToPositiveNumber: StringToPositiveNumberC = new t.Type<number, string, unknown>(
|
||||
'StringToPositiveNumber',
|
||||
t.number.is,
|
||||
(input, context): Either<t.Errors, number> => {
|
||||
return either.chain(
|
||||
t.string.validate(input, context),
|
||||
(numberAsString): Either<t.Errors, number> => {
|
||||
const stringAsNumber = +numberAsString;
|
||||
if (numberAsString.trim().length === 0 || isNaN(stringAsNumber) || stringAsNumber <= 0) {
|
||||
return t.failure(input, context);
|
||||
} else {
|
||||
return t.success(stringAsNumber);
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
String
|
||||
);
|
|
@ -68,7 +68,7 @@ describe('Exceptions Lists API', () => {
|
|||
});
|
||||
expect(fetchMock).toHaveBeenCalledWith('/api/exception_lists', {
|
||||
body:
|
||||
'{"_tags":["endpoint","process","malware","os:linux"],"created_at":"2020-04-23T00:19:13.289Z","created_by":"user_name","description":"This is a sample endpoint type exception","id":"1","list_id":"endpoint_list","meta":{},"name":"Sample Endpoint Exception List","tags":["user added string for a tag","malware"],"tie_breaker_id":"77fd1909-6786-428a-a671-30229a719c1f","type":"endpoint","updated_at":"2020-04-23T00:19:13.289Z","updated_by":"user_name"}',
|
||||
'{"_tags":["endpoint","process","malware","os:linux"],"created_at":"2020-04-23T00:19:13.289Z","created_by":"user_name","description":"This is a sample endpoint type exception","id":"1","list_id":"endpoint_list","meta":{},"name":"Sample Endpoint Exception List","namespace_type":"single","tags":["user added string for a tag","malware"],"tie_breaker_id":"77fd1909-6786-428a-a671-30229a719c1f","type":"endpoint","updated_at":"2020-04-23T00:19:13.289Z","updated_by":"user_name"}',
|
||||
method: 'PUT',
|
||||
signal: abortCtrl.signal,
|
||||
});
|
||||
|
@ -112,7 +112,7 @@ describe('Exceptions Lists API', () => {
|
|||
});
|
||||
expect(fetchMock).toHaveBeenCalledWith('/api/exception_lists/items', {
|
||||
body:
|
||||
'{"_tags":["endpoint","process","malware","os:linux"],"comment":[],"created_at":"2020-04-23T00:19:13.289Z","created_by":"user_name","description":"This is a sample endpoint type exception","entries":[{"field":"actingProcess.file.signer","match":"Elastic, N.V.","operator":"included"},{"field":"event.category","match_any":["process","malware"],"operator":"included"}],"id":"1","item_id":"endpoint_list_item","list_id":"endpoint_list","meta":{},"name":"Sample Endpoint Exception List","tags":["user added string for a tag","malware"],"tie_breaker_id":"77fd1909-6786-428a-a671-30229a719c1f","type":"simple","updated_at":"2020-04-23T00:19:13.289Z","updated_by":"user_name"}',
|
||||
'{"_tags":["endpoint","process","malware","os:linux"],"comment":[],"created_at":"2020-04-23T00:19:13.289Z","created_by":"user_name","description":"This is a sample endpoint type exception","entries":[{"field":"actingProcess.file.signer","match":"Elastic, N.V.","operator":"included"},{"field":"event.category","match_any":["process","malware"],"operator":"included"}],"id":"1","item_id":"endpoint_list_item","list_id":"endpoint_list","meta":{},"name":"Sample Endpoint Exception List","namespace_type":"single","tags":["user added string for a tag","malware"],"tie_breaker_id":"77fd1909-6786-428a-a671-30229a719c1f","type":"simple","updated_at":"2020-04-23T00:19:13.289Z","updated_by":"user_name"}',
|
||||
method: 'PUT',
|
||||
signal: abortCtrl.signal,
|
||||
});
|
||||
|
|
|
@ -69,6 +69,7 @@ describe('useExceptionList', () => {
|
|||
list_id: 'endpoint_list',
|
||||
meta: {},
|
||||
name: 'Sample Endpoint Exception List',
|
||||
namespace_type: 'single',
|
||||
tags: ['user added string for a tag', 'malware'],
|
||||
tie_breaker_id: '77fd1909-6786-428a-a671-30229a719c1f',
|
||||
type: 'simple',
|
||||
|
@ -84,6 +85,7 @@ describe('useExceptionList', () => {
|
|||
list_id: 'endpoint_list',
|
||||
meta: {},
|
||||
name: 'Sample Endpoint Exception List',
|
||||
namespace_type: 'single',
|
||||
tags: ['user added string for a tag', 'malware'],
|
||||
tie_breaker_id: '77fd1909-6786-428a-a671-30229a719c1f',
|
||||
type: 'endpoint',
|
||||
|
|
|
@ -19,6 +19,7 @@ export const mockExceptionList: ExceptionListSchema = {
|
|||
list_id: 'endpoint_list',
|
||||
meta: {},
|
||||
name: 'Sample Endpoint Exception List',
|
||||
namespace_type: 'single',
|
||||
tags: ['user added string for a tag', 'malware'],
|
||||
tie_breaker_id: '77fd1909-6786-428a-a671-30229a719c1f',
|
||||
type: 'endpoint',
|
||||
|
@ -84,6 +85,7 @@ export const mockExceptionItem: ExceptionListItemSchema = {
|
|||
list_id: 'endpoint_list',
|
||||
meta: {},
|
||||
name: 'Sample Endpoint Exception List',
|
||||
namespace_type: 'single',
|
||||
tags: ['user added string for a tag', 'malware'],
|
||||
tie_breaker_id: '77fd1909-6786-428a-a671-30229a719c1f',
|
||||
type: 'simple',
|
||||
|
|
|
@ -39,6 +39,7 @@ export const createExceptionListItemRoute = (router: IRouter): void => {
|
|||
const siemResponse = buildSiemResponse(response);
|
||||
try {
|
||||
const {
|
||||
namespace_type: namespaceType,
|
||||
name,
|
||||
_tags,
|
||||
tags,
|
||||
|
@ -54,8 +55,7 @@ export const createExceptionListItemRoute = (router: IRouter): void => {
|
|||
const exceptionList = await exceptionLists.getExceptionList({
|
||||
id: undefined,
|
||||
listId,
|
||||
// TODO: Expose the name space type
|
||||
namespaceType: 'single',
|
||||
namespaceType,
|
||||
});
|
||||
if (exceptionList == null) {
|
||||
return siemResponse.error({
|
||||
|
@ -66,8 +66,7 @@ export const createExceptionListItemRoute = (router: IRouter): void => {
|
|||
const exceptionListItem = await exceptionLists.getExceptionListItem({
|
||||
id: undefined,
|
||||
itemId,
|
||||
// TODO: Expose the name space type
|
||||
namespaceType: 'single',
|
||||
namespaceType,
|
||||
});
|
||||
if (exceptionListItem != null) {
|
||||
return siemResponse.error({
|
||||
|
@ -84,8 +83,7 @@ export const createExceptionListItemRoute = (router: IRouter): void => {
|
|||
listId,
|
||||
meta,
|
||||
name,
|
||||
// TODO: Expose the name space type
|
||||
namespaceType: 'single',
|
||||
namespaceType,
|
||||
tags,
|
||||
type,
|
||||
});
|
||||
|
|
|
@ -38,13 +38,21 @@ export const createExceptionListRoute = (router: IRouter): void => {
|
|||
async (context, request, response) => {
|
||||
const siemResponse = buildSiemResponse(response);
|
||||
try {
|
||||
const { name, _tags, tags, meta, description, list_id: listId, type } = request.body;
|
||||
const {
|
||||
name,
|
||||
_tags,
|
||||
tags,
|
||||
meta,
|
||||
namespace_type: namespaceType,
|
||||
description,
|
||||
list_id: listId,
|
||||
type,
|
||||
} = request.body;
|
||||
const exceptionLists = getExceptionListClient(context);
|
||||
const exceptionList = await exceptionLists.getExceptionList({
|
||||
id: undefined,
|
||||
listId,
|
||||
// TODO: Expose the name space type
|
||||
namespaceType: 'single',
|
||||
namespaceType,
|
||||
});
|
||||
if (exceptionList != null) {
|
||||
return siemResponse.error({
|
||||
|
@ -58,8 +66,7 @@ export const createExceptionListRoute = (router: IRouter): void => {
|
|||
listId,
|
||||
meta,
|
||||
name,
|
||||
// TODO: Expose the name space type
|
||||
namespaceType: 'single',
|
||||
namespaceType,
|
||||
tags,
|
||||
type,
|
||||
});
|
||||
|
|
|
@ -13,7 +13,11 @@ import {
|
|||
transformError,
|
||||
validate,
|
||||
} from '../siem_server_deps';
|
||||
import { deleteExceptionListItemSchema, exceptionListItemSchema } from '../../common/schemas';
|
||||
import {
|
||||
DeleteExceptionListItemSchemaDecoded,
|
||||
deleteExceptionListItemSchema,
|
||||
exceptionListItemSchema,
|
||||
} from '../../common/schemas';
|
||||
|
||||
import { getErrorMessageExceptionListItem, getExceptionListClient } from './utils';
|
||||
|
||||
|
@ -25,14 +29,17 @@ export const deleteExceptionListItemRoute = (router: IRouter): void => {
|
|||
},
|
||||
path: EXCEPTION_LIST_ITEM_URL,
|
||||
validate: {
|
||||
query: buildRouteValidation(deleteExceptionListItemSchema),
|
||||
query: buildRouteValidation<
|
||||
typeof deleteExceptionListItemSchema,
|
||||
DeleteExceptionListItemSchemaDecoded
|
||||
>(deleteExceptionListItemSchema),
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
const siemResponse = buildSiemResponse(response);
|
||||
try {
|
||||
const exceptionLists = getExceptionListClient(context);
|
||||
const { item_id: itemId, id } = request.query;
|
||||
const { item_id: itemId, id, namespace_type: namespaceType } = request.query;
|
||||
if (itemId == null && id == null) {
|
||||
return siemResponse.error({
|
||||
body: 'Either "item_id" or "id" needs to be defined in the request',
|
||||
|
@ -42,7 +49,7 @@ export const deleteExceptionListItemRoute = (router: IRouter): void => {
|
|||
const deleted = await exceptionLists.deleteExceptionListItem({
|
||||
id,
|
||||
itemId,
|
||||
namespaceType: 'single', // TODO: Bubble this up
|
||||
namespaceType,
|
||||
});
|
||||
if (deleted == null) {
|
||||
return siemResponse.error({
|
||||
|
|
|
@ -13,7 +13,11 @@ import {
|
|||
transformError,
|
||||
validate,
|
||||
} from '../siem_server_deps';
|
||||
import { deleteExceptionListSchema, exceptionListSchema } from '../../common/schemas';
|
||||
import {
|
||||
DeleteExceptionListSchemaDecoded,
|
||||
deleteExceptionListSchema,
|
||||
exceptionListSchema,
|
||||
} from '../../common/schemas';
|
||||
|
||||
import { getErrorMessageExceptionList, getExceptionListClient } from './utils';
|
||||
|
||||
|
@ -25,25 +29,27 @@ export const deleteExceptionListRoute = (router: IRouter): void => {
|
|||
},
|
||||
path: EXCEPTION_LIST_URL,
|
||||
validate: {
|
||||
query: buildRouteValidation(deleteExceptionListSchema),
|
||||
query: buildRouteValidation<
|
||||
typeof deleteExceptionListSchema,
|
||||
DeleteExceptionListSchemaDecoded
|
||||
>(deleteExceptionListSchema),
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
const siemResponse = buildSiemResponse(response);
|
||||
try {
|
||||
const exceptionLists = getExceptionListClient(context);
|
||||
const { list_id: listId, id } = request.query;
|
||||
const { list_id: listId, id, namespace_type: namespaceType } = request.query;
|
||||
if (listId == null && id == null) {
|
||||
return siemResponse.error({
|
||||
body: 'Either "list_id" or "id" needs to be defined in the request',
|
||||
statusCode: 400,
|
||||
});
|
||||
} else {
|
||||
// TODO: At the moment this will delete the list but we need to delete all the list items before deleting the list
|
||||
const deleted = await exceptionLists.deleteExceptionList({
|
||||
id,
|
||||
listId,
|
||||
namespaceType: 'single',
|
||||
namespaceType,
|
||||
});
|
||||
if (deleted == null) {
|
||||
return siemResponse.error({
|
||||
|
|
|
@ -13,7 +13,11 @@ import {
|
|||
transformError,
|
||||
validate,
|
||||
} from '../siem_server_deps';
|
||||
import { findExceptionListItemSchema, foundExceptionListItemSchema } from '../../common/schemas';
|
||||
import {
|
||||
FindExceptionListItemSchemaDecoded,
|
||||
findExceptionListItemSchema,
|
||||
foundExceptionListItemSchema,
|
||||
} from '../../common/schemas';
|
||||
|
||||
import { getExceptionListClient } from './utils';
|
||||
|
||||
|
@ -25,7 +29,10 @@ export const findExceptionListItemRoute = (router: IRouter): void => {
|
|||
},
|
||||
path: `${EXCEPTION_LIST_ITEM_URL}/_find`,
|
||||
validate: {
|
||||
query: buildRouteValidation(findExceptionListItemSchema),
|
||||
query: buildRouteValidation<
|
||||
typeof findExceptionListItemSchema,
|
||||
FindExceptionListItemSchemaDecoded
|
||||
>(findExceptionListItemSchema),
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
|
@ -35,6 +42,7 @@ export const findExceptionListItemRoute = (router: IRouter): void => {
|
|||
const {
|
||||
filter,
|
||||
list_id: listId,
|
||||
namespace_type: namespaceType,
|
||||
page,
|
||||
per_page: perPage,
|
||||
sort_field: sortField,
|
||||
|
@ -43,7 +51,7 @@ export const findExceptionListItemRoute = (router: IRouter): void => {
|
|||
const exceptionListItems = await exceptionLists.findExceptionListItem({
|
||||
filter,
|
||||
listId,
|
||||
namespaceType: 'single', // TODO: Bubble this up
|
||||
namespaceType,
|
||||
page,
|
||||
perPage,
|
||||
sortField,
|
||||
|
|
|
@ -13,7 +13,11 @@ import {
|
|||
transformError,
|
||||
validate,
|
||||
} from '../siem_server_deps';
|
||||
import { findExceptionListSchema, foundExceptionListSchema } from '../../common/schemas';
|
||||
import {
|
||||
FindExceptionListSchemaDecoded,
|
||||
findExceptionListSchema,
|
||||
foundExceptionListSchema,
|
||||
} from '../../common/schemas';
|
||||
|
||||
import { getExceptionListClient } from './utils';
|
||||
|
||||
|
@ -25,7 +29,9 @@ export const findExceptionListRoute = (router: IRouter): void => {
|
|||
},
|
||||
path: `${EXCEPTION_LIST_URL}/_find`,
|
||||
validate: {
|
||||
query: buildRouteValidation(findExceptionListSchema),
|
||||
query: buildRouteValidation<typeof findExceptionListSchema, FindExceptionListSchemaDecoded>(
|
||||
findExceptionListSchema
|
||||
),
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
|
@ -35,13 +41,14 @@ export const findExceptionListRoute = (router: IRouter): void => {
|
|||
const {
|
||||
filter,
|
||||
page,
|
||||
namespace_type: namespaceType,
|
||||
per_page: perPage,
|
||||
sort_field: sortField,
|
||||
sort_order: sortOrder,
|
||||
} = request.query;
|
||||
const exceptionListItems = await exceptionLists.findExceptionList({
|
||||
filter,
|
||||
namespaceType: 'single', // TODO: Bubble this up
|
||||
namespaceType,
|
||||
page,
|
||||
perPage,
|
||||
sortField,
|
||||
|
|
99
x-pack/plugins/lists/server/routes/find_list_item_route.ts
Normal file
99
x-pack/plugins/lists/server/routes/find_list_item_route.ts
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { IRouter } from 'kibana/server';
|
||||
|
||||
import { LIST_ITEM_URL } from '../../common/constants';
|
||||
import {
|
||||
buildRouteValidation,
|
||||
buildSiemResponse,
|
||||
transformError,
|
||||
validate,
|
||||
} from '../siem_server_deps';
|
||||
import { findListItemSchema, foundListItemSchema } from '../../common/schemas';
|
||||
import { decodeCursor } from '../services/utils';
|
||||
|
||||
import { getListClient } from './utils';
|
||||
|
||||
export const findListItemRoute = (router: IRouter): void => {
|
||||
router.get(
|
||||
{
|
||||
options: {
|
||||
tags: ['access:lists'],
|
||||
},
|
||||
path: `${LIST_ITEM_URL}/_find`,
|
||||
validate: {
|
||||
query: buildRouteValidation(findListItemSchema),
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
const siemResponse = buildSiemResponse(response);
|
||||
try {
|
||||
const lists = getListClient(context);
|
||||
const {
|
||||
cursor,
|
||||
filter: filterOrUndefined,
|
||||
list_id: listId,
|
||||
page: pageOrUndefined,
|
||||
per_page: perPageOrUndefined,
|
||||
sort_field: sortField,
|
||||
sort_order: sortOrder,
|
||||
} = request.query;
|
||||
|
||||
const page = pageOrUndefined ?? 1;
|
||||
const perPage = perPageOrUndefined ?? 20;
|
||||
const filter = filterOrUndefined ?? '';
|
||||
const {
|
||||
isValid,
|
||||
errorMessage,
|
||||
cursor: [currentIndexPosition, searchAfter],
|
||||
} = decodeCursor({
|
||||
cursor,
|
||||
page,
|
||||
perPage,
|
||||
sortField,
|
||||
});
|
||||
|
||||
if (!isValid) {
|
||||
return siemResponse.error({
|
||||
body: errorMessage,
|
||||
statusCode: 400,
|
||||
});
|
||||
} else {
|
||||
const exceptionList = await lists.findListItem({
|
||||
currentIndexPosition,
|
||||
filter,
|
||||
listId,
|
||||
page,
|
||||
perPage,
|
||||
searchAfter,
|
||||
sortField,
|
||||
sortOrder,
|
||||
});
|
||||
if (exceptionList == null) {
|
||||
return siemResponse.error({
|
||||
body: `list id: "${listId}" does not exist`,
|
||||
statusCode: 404,
|
||||
});
|
||||
} else {
|
||||
const [validated, errors] = validate(exceptionList, foundListItemSchema);
|
||||
if (errors != null) {
|
||||
return siemResponse.error({ body: errors, statusCode: 500 });
|
||||
} else {
|
||||
return response.ok({ body: validated ?? {} });
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
const error = transformError(err);
|
||||
return siemResponse.error({
|
||||
body: error.message,
|
||||
statusCode: error.statusCode,
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
89
x-pack/plugins/lists/server/routes/find_list_route.ts
Normal file
89
x-pack/plugins/lists/server/routes/find_list_route.ts
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { IRouter } from 'kibana/server';
|
||||
|
||||
import { LIST_URL } from '../../common/constants';
|
||||
import {
|
||||
buildRouteValidation,
|
||||
buildSiemResponse,
|
||||
transformError,
|
||||
validate,
|
||||
} from '../siem_server_deps';
|
||||
import { findListSchema, foundListSchema } from '../../common/schemas';
|
||||
import { decodeCursor } from '../services/utils';
|
||||
|
||||
import { getListClient } from './utils';
|
||||
|
||||
export const findListRoute = (router: IRouter): void => {
|
||||
router.get(
|
||||
{
|
||||
options: {
|
||||
tags: ['access:lists'],
|
||||
},
|
||||
path: `${LIST_URL}/_find`,
|
||||
validate: {
|
||||
query: buildRouteValidation(findListSchema),
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
const siemResponse = buildSiemResponse(response);
|
||||
try {
|
||||
const lists = getListClient(context);
|
||||
const {
|
||||
cursor,
|
||||
filter: filterOrUndefined,
|
||||
page: pageOrUndefined,
|
||||
per_page: perPageOrUndefined,
|
||||
sort_field: sortField,
|
||||
sort_order: sortOrder,
|
||||
} = request.query;
|
||||
|
||||
const page = pageOrUndefined ?? 1;
|
||||
const perPage = perPageOrUndefined ?? 20;
|
||||
const filter = filterOrUndefined ?? '';
|
||||
const {
|
||||
isValid,
|
||||
errorMessage,
|
||||
cursor: [currentIndexPosition, searchAfter],
|
||||
} = decodeCursor({
|
||||
cursor,
|
||||
page,
|
||||
perPage,
|
||||
sortField,
|
||||
});
|
||||
if (!isValid) {
|
||||
return siemResponse.error({
|
||||
body: errorMessage,
|
||||
statusCode: 400,
|
||||
});
|
||||
} else {
|
||||
const exceptionList = await lists.findList({
|
||||
currentIndexPosition,
|
||||
filter,
|
||||
page,
|
||||
perPage,
|
||||
searchAfter,
|
||||
sortField,
|
||||
sortOrder,
|
||||
});
|
||||
const [validated, errors] = validate(exceptionList, foundListSchema);
|
||||
if (errors != null) {
|
||||
return siemResponse.error({ body: errors, statusCode: 500 });
|
||||
} else {
|
||||
return response.ok({ body: validated ?? {} });
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
const error = transformError(err);
|
||||
return siemResponse.error({
|
||||
body: error.message,
|
||||
statusCode: error.statusCode,
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
|
@ -17,6 +17,8 @@ export * from './delete_list_route';
|
|||
export * from './export_list_item_route';
|
||||
export * from './find_exception_list_item_route';
|
||||
export * from './find_exception_list_route';
|
||||
export * from './find_list_item_route';
|
||||
export * from './find_list_route';
|
||||
export * from './import_list_item_route';
|
||||
export * from './init_routes';
|
||||
export * from './patch_list_item_route';
|
||||
|
|
|
@ -20,6 +20,8 @@ import {
|
|||
exportListItemRoute,
|
||||
findExceptionListItemRoute,
|
||||
findExceptionListRoute,
|
||||
findListItemRoute,
|
||||
findListRoute,
|
||||
importListItemRoute,
|
||||
patchListItemRoute,
|
||||
patchListRoute,
|
||||
|
@ -41,6 +43,7 @@ export const initRoutes = (router: IRouter): void => {
|
|||
updateListRoute(router);
|
||||
deleteListRoute(router);
|
||||
patchListRoute(router);
|
||||
findListRoute(router);
|
||||
|
||||
// list items
|
||||
createListItemRoute(router);
|
||||
|
@ -50,6 +53,7 @@ export const initRoutes = (router: IRouter): void => {
|
|||
patchListItemRoute(router);
|
||||
exportListItemRoute(router);
|
||||
importListItemRoute(router);
|
||||
findListItemRoute(router);
|
||||
|
||||
// indexes of lists
|
||||
createListIndexRoute(router);
|
||||
|
|
|
@ -13,7 +13,11 @@ import {
|
|||
transformError,
|
||||
validate,
|
||||
} from '../siem_server_deps';
|
||||
import { exceptionListItemSchema, readExceptionListItemSchema } from '../../common/schemas';
|
||||
import {
|
||||
ReadExceptionListItemSchemaDecoded,
|
||||
exceptionListItemSchema,
|
||||
readExceptionListItemSchema,
|
||||
} from '../../common/schemas';
|
||||
|
||||
import { getErrorMessageExceptionListItem, getExceptionListClient } from './utils';
|
||||
|
||||
|
@ -25,20 +29,22 @@ export const readExceptionListItemRoute = (router: IRouter): void => {
|
|||
},
|
||||
path: EXCEPTION_LIST_ITEM_URL,
|
||||
validate: {
|
||||
query: buildRouteValidation(readExceptionListItemSchema),
|
||||
query: buildRouteValidation<
|
||||
typeof readExceptionListItemSchema,
|
||||
ReadExceptionListItemSchemaDecoded
|
||||
>(readExceptionListItemSchema),
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
const siemResponse = buildSiemResponse(response);
|
||||
try {
|
||||
const { id, item_id: itemId } = request.query;
|
||||
const { id, item_id: itemId, namespace_type: namespaceType } = request.query;
|
||||
const exceptionLists = getExceptionListClient(context);
|
||||
if (id != null || itemId != null) {
|
||||
const exceptionListItem = await exceptionLists.getExceptionListItem({
|
||||
id,
|
||||
itemId,
|
||||
// TODO: Bubble this up
|
||||
namespaceType: 'single',
|
||||
namespaceType,
|
||||
});
|
||||
if (exceptionListItem == null) {
|
||||
return siemResponse.error({
|
||||
|
|
|
@ -13,7 +13,11 @@ import {
|
|||
transformError,
|
||||
validate,
|
||||
} from '../siem_server_deps';
|
||||
import { exceptionListSchema, readExceptionListSchema } from '../../common/schemas';
|
||||
import {
|
||||
ReadExceptionListSchemaDecoded,
|
||||
exceptionListSchema,
|
||||
readExceptionListSchema,
|
||||
} from '../../common/schemas';
|
||||
|
||||
import { getErrorMessageExceptionList, getExceptionListClient } from './utils';
|
||||
|
||||
|
@ -25,20 +29,21 @@ export const readExceptionListRoute = (router: IRouter): void => {
|
|||
},
|
||||
path: EXCEPTION_LIST_URL,
|
||||
validate: {
|
||||
query: buildRouteValidation(readExceptionListSchema),
|
||||
query: buildRouteValidation<typeof readExceptionListSchema, ReadExceptionListSchemaDecoded>(
|
||||
readExceptionListSchema
|
||||
),
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
const siemResponse = buildSiemResponse(response);
|
||||
try {
|
||||
const { id, list_id: listId } = request.query;
|
||||
const { id, list_id: listId, namespace_type: namespaceType } = request.query;
|
||||
const exceptionLists = getExceptionListClient(context);
|
||||
if (id != null || listId != null) {
|
||||
const exceptionList = await exceptionLists.getExceptionList({
|
||||
id,
|
||||
listId,
|
||||
// TODO: Bubble this up
|
||||
namespaceType: 'single',
|
||||
namespaceType,
|
||||
});
|
||||
if (exceptionList == null) {
|
||||
return siemResponse.error({
|
||||
|
|
|
@ -31,7 +31,7 @@ export const readListIndexRoute = (router: IRouter): void => {
|
|||
|
||||
if (listIndexExists || listItemIndexExists) {
|
||||
const [validated, errors] = validate(
|
||||
{ list_index: listIndexExists, lists_item_index: listItemIndexExists },
|
||||
{ list_index: listIndexExists, list_item_index: listItemIndexExists },
|
||||
listItemIndexExistSchema
|
||||
);
|
||||
if (errors != null) {
|
||||
|
|
|
@ -48,6 +48,7 @@ export const updateExceptionListItemRoute = (router: IRouter): void => {
|
|||
comment,
|
||||
entries,
|
||||
item_id: itemId,
|
||||
namespace_type: namespaceType,
|
||||
tags,
|
||||
} = request.body;
|
||||
const exceptionLists = getExceptionListClient(context);
|
||||
|
@ -60,7 +61,7 @@ export const updateExceptionListItemRoute = (router: IRouter): void => {
|
|||
itemId,
|
||||
meta,
|
||||
name,
|
||||
namespaceType: 'single', // TODO: Bubble this up
|
||||
namespaceType,
|
||||
tags,
|
||||
type,
|
||||
});
|
||||
|
|
|
@ -38,7 +38,17 @@ export const updateExceptionListRoute = (router: IRouter): void => {
|
|||
async (context, request, response) => {
|
||||
const siemResponse = buildSiemResponse(response);
|
||||
try {
|
||||
const { _tags, tags, name, description, id, list_id: listId, meta, type } = request.body;
|
||||
const {
|
||||
_tags,
|
||||
tags,
|
||||
name,
|
||||
description,
|
||||
id,
|
||||
list_id: listId,
|
||||
meta,
|
||||
namespace_type: namespaceType,
|
||||
type,
|
||||
} = request.body;
|
||||
const exceptionLists = getExceptionListClient(context);
|
||||
if (id == null && listId == null) {
|
||||
return siemResponse.error({
|
||||
|
@ -53,7 +63,7 @@ export const updateExceptionListRoute = (router: IRouter): void => {
|
|||
listId,
|
||||
meta,
|
||||
name,
|
||||
namespaceType: 'single', // TODO: Bubble this up
|
||||
namespaceType,
|
||||
tags,
|
||||
type,
|
||||
});
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License;
|
||||
# you may not use this file except in compliance with the Elastic License.
|
||||
#
|
||||
|
||||
set -e
|
||||
./check_env_variables.sh
|
||||
|
||||
# Example: ./delete_all_lists.sh
|
||||
# https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete-by-query.html
|
||||
|
||||
|
||||
# Delete all the main lists that have children items
|
||||
curl -s -k \
|
||||
-H "Content-Type: application/json" \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X POST ${ELASTICSEARCH_URL}/${KIBANA_INDEX}*/_delete_by_query \
|
||||
--data '{
|
||||
"query": {
|
||||
"exists": { "field": "siem_list" }
|
||||
}
|
||||
}' \
|
||||
| jq .
|
||||
|
||||
# Delete all the list children items as well
|
||||
curl -s -k \
|
||||
-H "Content-Type: application/json" \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X POST ${ELASTICSEARCH_URL}/${KIBANA_INDEX}*/_delete_by_query \
|
||||
--data '{
|
||||
"query": {
|
||||
"exists": { "field": "siem_list_item" }
|
||||
}
|
||||
}' \
|
||||
| jq .
|
|
@ -9,8 +9,12 @@
|
|||
set -e
|
||||
./check_env_variables.sh
|
||||
|
||||
NAMESPACE_TYPE=${2-single}
|
||||
|
||||
# Example: ./delete_exception_list.sh ${list_id}
|
||||
# Example: ./delete_exception_list.sh ${list_id} single
|
||||
# Example: ./delete_exception_list.sh ${list_id} agnostic
|
||||
curl -s -k \
|
||||
-H 'kbn-xsrf: 123' \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X DELETE ${KIBANA_URL}${SPACE_URL}/api/exception_lists?list_id="$1" | jq .
|
||||
-X DELETE "${KIBANA_URL}${SPACE_URL}/api/exception_lists?list_id=$1&namespace_type=${NAMESPACE_TYPE}" | jq .
|
||||
|
|
|
@ -9,8 +9,12 @@
|
|||
set -e
|
||||
./check_env_variables.sh
|
||||
|
||||
NAMESPACE_TYPE=${2-single}
|
||||
|
||||
# Example: ./delete_exception_list_by_id.sh ${list_id}
|
||||
# Example: ./delete_exception_list_by_id.sh ${list_id} single
|
||||
# Example: ./delete_exception_list_by_id.sh ${list_id} agnostic
|
||||
curl -s -k \
|
||||
-H 'kbn-xsrf: 123' \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X DELETE ${KIBANA_URL}${SPACE_URL}/api/exception_lists?id="$1" | jq .
|
||||
-X DELETE "${KIBANA_URL}${SPACE_URL}/api/exception_lists?id=$1&namespace_type=${NAMESPACE_TYPE}" | jq .
|
||||
|
|
|
@ -9,8 +9,12 @@
|
|||
set -e
|
||||
./check_env_variables.sh
|
||||
|
||||
NAMESPACE_TYPE=${2-single}
|
||||
|
||||
# Example: ./delete_exception_list_item.sh ${item_id}
|
||||
# Example: ./delete_exception_list_item.sh ${item_id} single
|
||||
# Example: ./delete_exception_list_item.sh ${item_id} agnostic
|
||||
curl -s -k \
|
||||
-H 'kbn-xsrf: 123' \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X DELETE ${KIBANA_URL}${SPACE_URL}/api/exception_lists/items?item_id="$1" | jq .
|
||||
-X DELETE "${KIBANA_URL}${SPACE_URL}/api/exception_lists/items?item_id=$1&namespace_type=${NAMESPACE_TYPE}" | jq .
|
||||
|
|
|
@ -9,8 +9,12 @@
|
|||
set -e
|
||||
./check_env_variables.sh
|
||||
|
||||
NAMESPACE_TYPE=${2-single}
|
||||
|
||||
# Example: ./delete_exception_list_item_by_id.sh ${list_id}
|
||||
# Example: ./delete_exception_list_item_by_id.sh ${list_id} single
|
||||
# Example: ./delete_exception_list_item_by_id.sh ${list_id} agnostic
|
||||
curl -s -k \
|
||||
-H 'kbn-xsrf: 123' \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X DELETE ${KIBANA_URL}${SPACE_URL}/api/exception_lists/items?id="$1" | jq .
|
||||
-X DELETE "${KIBANA_URL}${SPACE_URL}/api/exception_lists/items?id=$1&namespace_type=${NAMESPACE_TYPE}" | jq .
|
||||
|
|
|
@ -13,4 +13,4 @@ set -e
|
|||
curl -s -k \
|
||||
-H 'kbn-xsrf: 123' \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X DELETE ${KIBANA_URL}${SPACE_URL}/api/lists?id="$1" | jq .
|
||||
-X DELETE "${KIBANA_URL}${SPACE_URL}/api/lists?id=$1" | jq .
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"list_id": "endpoint_list",
|
||||
"_tags": ["endpoint", "process", "malware", "os:linux"],
|
||||
"tags": ["user added string for a tag", "malware"],
|
||||
"type": "endpoint",
|
||||
"description": "This is a sample agnostic endpoint type exception",
|
||||
"name": "Sample Endpoint Exception List",
|
||||
"namespace_type": "agnostic"
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"list_id": "endpoint_list",
|
||||
"item_id": "endpoint_list_item",
|
||||
"_tags": ["endpoint", "process", "malware", "os:linux"],
|
||||
"tags": ["user added string for a tag", "malware"],
|
||||
"type": "simple",
|
||||
"description": "This is a sample agnostic endpoint type exception",
|
||||
"name": "Sample Endpoint Exception List",
|
||||
"namespace_type": "agnostic",
|
||||
"entries": [
|
||||
{
|
||||
"field": "actingProcess.file.signer",
|
||||
"operator": "included",
|
||||
"match": "Elastic, N.V."
|
||||
},
|
||||
{
|
||||
"field": "event.category",
|
||||
"operator": "included",
|
||||
"match_any": ["process", "malware"]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"item_id": "endpoint_list_item",
|
||||
"_tags": ["endpoint", "process", "malware", "os:windows"],
|
||||
"tags": ["user added string for a tag", "malware"],
|
||||
"type": "simple",
|
||||
"description": "This is a sample agnostic change here this list",
|
||||
"name": "Sample Endpoint Exception List update change",
|
||||
"namespace_type": "agnostic",
|
||||
"entries": [
|
||||
{
|
||||
"field": "event.category",
|
||||
"operator": "included",
|
||||
"match_any": ["process", "malware"]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -10,7 +10,11 @@ set -e
|
|||
./check_env_variables.sh
|
||||
|
||||
LIST_ID=${1:-endpoint_list}
|
||||
NAMESPACE_TYPE=${2-single}
|
||||
|
||||
# Example: ./find_exception_list_items.sh {list-id}
|
||||
# Example: ./find_exception_list_items.sh {list-id} single
|
||||
# Example: ./find_exception_list_items.sh {list-id} agnostic
|
||||
curl -s -k \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X GET ${KIBANA_URL}${SPACE_URL}/api/exception_lists/items/_find?list_id=${LIST_ID} | jq .
|
||||
-X GET "${KIBANA_URL}${SPACE_URL}/api/exception_lists/items/_find?list_id=${LIST_ID}&namespace_type=${NAMESPACE_TYPE}" | jq .
|
||||
|
|
29
x-pack/plugins/lists/server/scripts/find_exception_list_items_by_filter.sh
Executable file
29
x-pack/plugins/lists/server/scripts/find_exception_list_items_by_filter.sh
Executable file
|
@ -0,0 +1,29 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License;
|
||||
# you may not use this file except in compliance with the Elastic License.
|
||||
#
|
||||
|
||||
set -e
|
||||
./check_env_variables.sh
|
||||
|
||||
LIST_ID=${1:-endpoint_list}
|
||||
FILTER=${2:-'exception-list.attributes.name:%20Sample%20Endpoint%20Exception%20List'}
|
||||
NAMESPACE_TYPE=${3-single}
|
||||
|
||||
# The %20 is just an encoded space that is typical of URL's.
|
||||
# The %22 is just an encoded quote of "
|
||||
# Table of them for testing if needed: https://www.w3schools.com/tags/ref_urlencode.asp
|
||||
|
||||
# Example: ./find_exception_list_items_by_filter.sh endpoint_list exception-list.attributes.name:%20Sample%20Endpoint%20Exception%20List
|
||||
# Example: ./find_exception_list_items_by_filter.sh endpoint_list exception-list.attributes.name:%20Sample%20Endpoint%20Exception%20List single
|
||||
# Example: ./find_exception_list_items_by_filter.sh endpoint_list exception-list.attributes.name:%20Sample%20Endpoint%20Exception%20List agnostic
|
||||
#
|
||||
# Example: ./find_exception_list_items_by_filter.sh endpoint_list exception-list.attributes.entries.field:actingProcess.file.signer
|
||||
# Example: ./find_exception_list_items_by_filter.sh endpoint_list "exception-list.attributes.entries.field:actingProcess.file.signe*"
|
||||
# Example: ./find_exception_list_items_by_filter.sh endpoint_list "exception-list.attributes.entries.match:Elastic*%20AND%20exception-list.attributes.entries.field:actingProcess.file.signe*"
|
||||
curl -s -k \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X GET "${KIBANA_URL}${SPACE_URL}/api/exception_lists/items/_find?list_id=${LIST_ID}&filter=${FILTER}&namespace_type=${NAMESPACE_TYPE}" | jq .
|
|
@ -9,7 +9,11 @@
|
|||
set -e
|
||||
./check_env_variables.sh
|
||||
|
||||
NAMESPACE_TYPE=${1-single}
|
||||
|
||||
# Example: ./find_exception_lists.sh {list-id}
|
||||
# Example: ./find_exception_lists.sh {list-id} single
|
||||
# Example: ./find_exception_lists.sh {list-id} agnostic
|
||||
curl -s -k \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X GET ${KIBANA_URL}${SPACE_URL}/api/exception_lists/_find | jq .
|
||||
-X GET "${KIBANA_URL}${SPACE_URL}/api/exception_lists/_find?namespace_type=${NAMESPACE_TYPE}" | jq .
|
||||
|
|
26
x-pack/plugins/lists/server/scripts/find_exception_lists_by_filter.sh
Executable file
26
x-pack/plugins/lists/server/scripts/find_exception_lists_by_filter.sh
Executable file
|
@ -0,0 +1,26 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License;
|
||||
# you may not use this file except in compliance with the Elastic License.
|
||||
#
|
||||
|
||||
set -e
|
||||
./check_env_variables.sh
|
||||
|
||||
FILTER=${1:-'exception-list.attributes.name:%20Sample%20Endpoint%20Exception%20List'}
|
||||
NAMESPACE_TYPE=${2-single}
|
||||
|
||||
# The %20 is just an encoded space that is typical of URL's.
|
||||
# The %22 is just an encoded quote of "
|
||||
# Table of them for testing if needed: https://www.w3schools.com/tags/ref_urlencode.asp
|
||||
|
||||
# Example get all lists by a particular name:
|
||||
# ./find_exception_lists_by_filter.sh exception-list.attributes.name:%20Sample%20Endpoint%20Exception%20List
|
||||
# ./find_exception_lists_by_filter.sh exception-list.attributes.tags:%20malware
|
||||
# ./find_exception_lists_by_filter.sh exception-list.attributes.tags:%20malware single
|
||||
# ./find_exception_lists_by_filter.sh exception-list.attributes.tags:%20malware agnostic
|
||||
curl -s -k \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X GET "${KIBANA_URL}${SPACE_URL}/api/exception_lists/_find?filter=${FILTER}&namespace_type=${NAMESPACE_TYPE}" | jq .
|
19
x-pack/plugins/lists/server/scripts/find_list_items.sh
Executable file
19
x-pack/plugins/lists/server/scripts/find_list_items.sh
Executable file
|
@ -0,0 +1,19 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License;
|
||||
# you may not use this file except in compliance with the Elastic License.
|
||||
#
|
||||
|
||||
set -e
|
||||
./check_env_variables.sh
|
||||
|
||||
PAGE=${1-1}
|
||||
PER_PAGE=${2-20}
|
||||
LIST_ID=${3-list-ip}
|
||||
|
||||
# Example: ./find_list_items.sh 1 20 list-ip
|
||||
curl -s -k \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X GET "${KIBANA_URL}${SPACE_URL}/api/lists/items/_find?list_id=${LIST_ID}&page=${PAGE}&per_page=${PER_PAGE}" | jq .
|
23
x-pack/plugins/lists/server/scripts/find_list_items_with_cursor.sh
Executable file
23
x-pack/plugins/lists/server/scripts/find_list_items_with_cursor.sh
Executable file
|
@ -0,0 +1,23 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License;
|
||||
# you may not use this file except in compliance with the Elastic License.
|
||||
#
|
||||
|
||||
set -e
|
||||
./check_env_variables.sh
|
||||
|
||||
PAGE=${1-1}
|
||||
PER_PAGE=${2-20}
|
||||
LIST_ID=${3-list-ip}
|
||||
CURSOR=${4-invalid}
|
||||
|
||||
# Example:
|
||||
# ./find_list_items.sh 1 20 | jq .cursor
|
||||
# Copy the cursor into the argument below like so
|
||||
# ./find_list_items_with_cursor.sh 1 10 list-ip eyJwYWdlX2luZGV4IjoyMCwic2VhcmNoX2FmdGVyIjpbIjAyZDZlNGY3LWUzMzAtNGZkYi1iNTY0LTEzZjNiOTk1MjRiYSJdfQ==
|
||||
curl -s -k \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X GET "${KIBANA_URL}${SPACE_URL}/api/lists/items/_find?list_id=${LIST_ID}&page=${PAGE}&per_page=${PER_PAGE}&cursor=${CURSOR}" | jq .
|
21
x-pack/plugins/lists/server/scripts/find_list_items_with_sort.sh
Executable file
21
x-pack/plugins/lists/server/scripts/find_list_items_with_sort.sh
Executable file
|
@ -0,0 +1,21 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License;
|
||||
# you may not use this file except in compliance with the Elastic License.
|
||||
#
|
||||
|
||||
set -e
|
||||
./check_env_variables.sh
|
||||
|
||||
PAGE=${1-1}
|
||||
PER_PAGE=${2-20}
|
||||
SORT_FIELD=${3-value}
|
||||
SORT_ORDER=${4-asc}
|
||||
LIST_ID=${5-list-ip}
|
||||
|
||||
# Example: ./find_list_items_with_sort.sh 1 20 value asc list-ip
|
||||
curl -s -k \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X GET "${KIBANA_URL}${SPACE_URL}/api/lists/items/_find?list_id=${LIST_ID}&page=${PAGE}&per_page=${PER_PAGE}&sort_field=${SORT_FIELD}&sort_order=${SORT_ORDER}" | jq .
|
22
x-pack/plugins/lists/server/scripts/find_list_items_with_sort_cursor.sh
Executable file
22
x-pack/plugins/lists/server/scripts/find_list_items_with_sort_cursor.sh
Executable file
|
@ -0,0 +1,22 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License;
|
||||
# you may not use this file except in compliance with the Elastic License.
|
||||
#
|
||||
|
||||
set -e
|
||||
./check_env_variables.sh
|
||||
|
||||
PAGE=${1-1}
|
||||
PER_PAGE=${2-20}
|
||||
SORT_FIELD=${3-value}
|
||||
SORT_ORDER=${4-asc}
|
||||
LIST_ID=${5-list-ip}
|
||||
CURSOR=${6-invalid}
|
||||
|
||||
# Example: ./find_list_items_with_sort_cursor.sh 1 20 value asc list-ip <cursor>
|
||||
curl -s -k \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X GET "${KIBANA_URL}${SPACE_URL}/api/lists/items/_find?list_id=${LIST_ID}&page=${PAGE}&per_page=${PER_PAGE}&sort_field=${SORT_FIELD}&sort_order=${SORT_ORDER}&cursor=${CURSOR}" | jq .
|
18
x-pack/plugins/lists/server/scripts/find_lists.sh
Executable file
18
x-pack/plugins/lists/server/scripts/find_lists.sh
Executable file
|
@ -0,0 +1,18 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License;
|
||||
# you may not use this file except in compliance with the Elastic License.
|
||||
#
|
||||
|
||||
set -e
|
||||
./check_env_variables.sh
|
||||
|
||||
PAGE=${1-1}
|
||||
PER_PAGE=${2-20}
|
||||
|
||||
# Example: ./find_lists.sh 1 20
|
||||
curl -s -k \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X GET "${KIBANA_URL}${SPACE_URL}/api/lists/_find?page=${PAGE}&per_page=${PER_PAGE}" | jq .
|
22
x-pack/plugins/lists/server/scripts/find_lists_with_cursor.sh
Executable file
22
x-pack/plugins/lists/server/scripts/find_lists_with_cursor.sh
Executable file
|
@ -0,0 +1,22 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License;
|
||||
# you may not use this file except in compliance with the Elastic License.
|
||||
#
|
||||
|
||||
set -e
|
||||
./check_env_variables.sh
|
||||
|
||||
PAGE=${1-1}
|
||||
PER_PAGE=${2-20}
|
||||
CURSOR=${3-invalid}
|
||||
|
||||
# Example:
|
||||
# ./find_lists.sh 1 20 | jq .cursor
|
||||
# Copy the cursor into the argument below like so
|
||||
# ./find_lists_with_cursor.sh 1 10 eyJwYWdlX2luZGV4IjoyMCwic2VhcmNoX2FmdGVyIjpbIjAyZDZlNGY3LWUzMzAtNGZkYi1iNTY0LTEzZjNiOTk1MjRiYSJdfQ==
|
||||
curl -s -k \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X GET "${KIBANA_URL}${SPACE_URL}/api/lists/_find?page=${PAGE}&per_page=${PER_PAGE}&cursor=${CURSOR}" | jq .
|
18
x-pack/plugins/lists/server/scripts/find_lists_with_filter.sh
Executable file
18
x-pack/plugins/lists/server/scripts/find_lists_with_filter.sh
Executable file
|
@ -0,0 +1,18 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License;
|
||||
# you may not use this file except in compliance with the Elastic License.
|
||||
#
|
||||
|
||||
set -e
|
||||
./check_env_variables.sh
|
||||
|
||||
PAGE=${1-1}
|
||||
PER_PAGE=${2-20}
|
||||
FILTER=${3-type:ip}
|
||||
# Example: ./find_lists_with_filter.sh 1 20 type:ip
|
||||
curl -s -k \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X GET "${KIBANA_URL}${SPACE_URL}/api/lists/_find?page=${PAGE}&per_page=${PER_PAGE}&filter=${FILTER}" | jq .
|
20
x-pack/plugins/lists/server/scripts/find_lists_with_sort.sh
Executable file
20
x-pack/plugins/lists/server/scripts/find_lists_with_sort.sh
Executable file
|
@ -0,0 +1,20 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License;
|
||||
# you may not use this file except in compliance with the Elastic License.
|
||||
#
|
||||
|
||||
set -e
|
||||
./check_env_variables.sh
|
||||
|
||||
PAGE=${1-1}
|
||||
PER_PAGE=${2-20}
|
||||
SORT_FIELD=${3-name}
|
||||
SORT_ORDER=${4-asc}
|
||||
|
||||
# Example: ./find_lists_with_sort.sh 1 20 name asc
|
||||
curl -s -k \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X GET "${KIBANA_URL}${SPACE_URL}/api/lists/_find?page=${PAGE}&per_page=${PER_PAGE}&sort_field=${SORT_FIELD}&sort_order=${SORT_ORDER}" | jq .
|
21
x-pack/plugins/lists/server/scripts/find_lists_with_sort_cursor.sh
Executable file
21
x-pack/plugins/lists/server/scripts/find_lists_with_sort_cursor.sh
Executable file
|
@ -0,0 +1,21 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License;
|
||||
# you may not use this file except in compliance with the Elastic License.
|
||||
#
|
||||
|
||||
set -e
|
||||
./check_env_variables.sh
|
||||
|
||||
PAGE=${1-1}
|
||||
PER_PAGE=${2-20}
|
||||
SORT_FIELD=${3-name}
|
||||
SORT_ORDER=${4-asc}
|
||||
CURSOR=${5-invalid}
|
||||
|
||||
# Example: ./find_lists_with_sort_cursor.sh 1 20 name asc <cursor>
|
||||
curl -s -k \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X GET "${KIBANA_URL}${SPACE_URL}/api/lists/_find?page=${PAGE}&per_page=${PER_PAGE}&sort_field=${SORT_FIELD}&sort_order=${SORT_ORDER}&cursor=${CURSOR}" | jq .
|
|
@ -9,7 +9,10 @@
|
|||
set -e
|
||||
./check_env_variables.sh
|
||||
|
||||
# Example: ./get_exception_list.sh {id}
|
||||
NAMESPACE_TYPE=${2-single}
|
||||
|
||||
# Example: ./get_exception_list.sh {id} single
|
||||
# Example: ./get_exception_list.sh {id} agnostic
|
||||
curl -s -k \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X GET ${KIBANA_URL}${SPACE_URL}/api/exception_lists?list_id="$1" | jq .
|
||||
-X GET "${KIBANA_URL}${SPACE_URL}/api/exception_lists?list_id=$1&namespace_type=${NAMESPACE_TYPE}" | jq .
|
||||
|
|
|
@ -9,7 +9,9 @@
|
|||
set -e
|
||||
./check_env_variables.sh
|
||||
|
||||
NAMESPACE_TYPE=${2-single}
|
||||
|
||||
# Example: ./get_exception_list_by_id.sh {id}
|
||||
curl -s -k \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X GET ${KIBANA_URL}${SPACE_URL}/api/exception_lists?id="$1" | jq .
|
||||
-X GET "${KIBANA_URL}${SPACE_URL}/api/exception_lists?id=$1&namespace_type=${NAMESPACE_TYPE}" | jq .
|
||||
|
|
|
@ -9,7 +9,11 @@
|
|||
set -e
|
||||
./check_env_variables.sh
|
||||
|
||||
NAMESPACE_TYPE=${2-single}
|
||||
|
||||
# Example: ./get_exception_list_item.sh {id}
|
||||
# Example: ./get_exception_list_item.sh {id} single
|
||||
# Example: ./get_exception_list_item.sh {id} agnostic
|
||||
curl -s -k \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X GET ${KIBANA_URL}${SPACE_URL}/api/exception_lists/items?item_id="$1" | jq .
|
||||
-X GET "${KIBANA_URL}${SPACE_URL}/api/exception_lists/items?item_id=$1&namespace_type=${NAMESPACE_TYPE}" | jq .
|
||||
|
|
|
@ -9,7 +9,11 @@
|
|||
set -e
|
||||
./check_env_variables.sh
|
||||
|
||||
NAMESPACE_TYPE=${2-single}
|
||||
|
||||
# Example: ./get_exception_list_item_by_id.sh {id}
|
||||
# Example: ./get_exception_list_item_by_id.sh {id} single
|
||||
# Example: ./get_exception_list_item_by_id.sh {id} agnostic
|
||||
curl -s -k \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X GET ${KIBANA_URL}${SPACE_URL}/api/exception_lists/items?id="$1" | jq .
|
||||
-X GET "${KIBANA_URL}${SPACE_URL}/api/exception_lists/items?id=$1&namespace_type=${NAMESPACE_TYPE}" | jq .
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "Simple list with a type of ip and an auto created id",
|
||||
"description": "list with an auto created id",
|
||||
"type": "ip"
|
||||
}
|
|
@ -15,12 +15,12 @@ import {
|
|||
ListId,
|
||||
MetaOrUndefined,
|
||||
Name,
|
||||
NamespaceType,
|
||||
Tags,
|
||||
_Tags,
|
||||
} from '../../../common/schemas';
|
||||
|
||||
import { getSavedObjectType, transformSavedObjectToExceptionList } from './utils';
|
||||
import { NamespaceType } from './types';
|
||||
|
||||
interface CreateExceptionListOptions {
|
||||
_tags: _Tags;
|
||||
|
@ -68,5 +68,5 @@ export const createExceptionList = async ({
|
|||
type,
|
||||
updated_by: user,
|
||||
});
|
||||
return transformSavedObjectToExceptionList({ savedObject });
|
||||
return transformSavedObjectToExceptionList({ namespaceType, savedObject });
|
||||
};
|
||||
|
|
|
@ -18,12 +18,12 @@ import {
|
|||
ListId,
|
||||
MetaOrUndefined,
|
||||
Name,
|
||||
NamespaceType,
|
||||
Tags,
|
||||
_Tags,
|
||||
} from '../../../common/schemas';
|
||||
|
||||
import { getSavedObjectType, transformSavedObjectToExceptionListItem } from './utils';
|
||||
import { NamespaceType } from './types';
|
||||
|
||||
interface CreateExceptionListItemOptions {
|
||||
_tags: _Tags;
|
||||
|
@ -77,5 +77,5 @@ export const createExceptionListItem = async ({
|
|||
type,
|
||||
updated_by: user,
|
||||
});
|
||||
return transformSavedObjectToExceptionListItem({ savedObject });
|
||||
return transformSavedObjectToExceptionListItem({ namespaceType, savedObject });
|
||||
};
|
||||
|
|
|
@ -6,10 +6,14 @@
|
|||
|
||||
import { SavedObjectsClientContract } from 'kibana/server';
|
||||
|
||||
import { ExceptionListSchema, IdOrUndefined, ListIdOrUndefined } from '../../../common/schemas';
|
||||
import {
|
||||
ExceptionListSchema,
|
||||
IdOrUndefined,
|
||||
ListIdOrUndefined,
|
||||
NamespaceType,
|
||||
} from '../../../common/schemas';
|
||||
|
||||
import { getSavedObjectType } from './utils';
|
||||
import { NamespaceType } from './types';
|
||||
import { getExceptionList } from './get_exception_list';
|
||||
import { deleteExceptionListItemByList } from './delete_exception_list_items_by_list';
|
||||
|
||||
|
|
|
@ -6,10 +6,14 @@
|
|||
|
||||
import { SavedObjectsClientContract } from 'kibana/server';
|
||||
|
||||
import { ExceptionListItemSchema, IdOrUndefined, ItemIdOrUndefined } from '../../../common/schemas';
|
||||
import {
|
||||
ExceptionListItemSchema,
|
||||
IdOrUndefined,
|
||||
ItemIdOrUndefined,
|
||||
NamespaceType,
|
||||
} from '../../../common/schemas';
|
||||
|
||||
import { getSavedObjectType } from './utils';
|
||||
import { NamespaceType } from './types';
|
||||
import { getExceptionListItem } from './get_exception_list_item';
|
||||
|
||||
interface DeleteExceptionListItemOptions {
|
||||
|
|
|
@ -5,10 +5,9 @@
|
|||
*/
|
||||
|
||||
import { SavedObjectsClientContract } from '../../../../../../src/core/server/';
|
||||
import { ListId } from '../../../common/schemas';
|
||||
import { ListId, NamespaceType } from '../../../common/schemas';
|
||||
|
||||
import { findExceptionListItem } from './find_exception_list_item';
|
||||
import { NamespaceType } from './types';
|
||||
import { getSavedObjectType } from './utils';
|
||||
|
||||
const PER_PAGE = 100;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import { SavedObjectsClientContract } from 'kibana/server';
|
||||
|
||||
import {
|
||||
ExceptionListItemSchema,
|
||||
ExceptionListSchema,
|
||||
FoundExceptionListItemSchema,
|
||||
FoundExceptionListSchema,
|
||||
|
@ -59,7 +60,7 @@ export class ExceptionListClient {
|
|||
itemId,
|
||||
id,
|
||||
namespaceType,
|
||||
}: GetExceptionListItemOptions): Promise<ExceptionListSchema | null> => {
|
||||
}: GetExceptionListItemOptions): Promise<ExceptionListItemSchema | null> => {
|
||||
const { savedObjectsClient } = this;
|
||||
return getExceptionListItem({ id, itemId, namespaceType, savedObjectsClient });
|
||||
};
|
||||
|
@ -142,7 +143,7 @@ export class ExceptionListClient {
|
|||
namespaceType,
|
||||
tags,
|
||||
type,
|
||||
}: CreateExceptionListItemOptions): Promise<ExceptionListSchema> => {
|
||||
}: CreateExceptionListItemOptions): Promise<ExceptionListItemSchema> => {
|
||||
const { savedObjectsClient, user } = this;
|
||||
return createExceptionListItem({
|
||||
_tags,
|
||||
|
@ -173,7 +174,7 @@ export class ExceptionListClient {
|
|||
namespaceType,
|
||||
tags,
|
||||
type,
|
||||
}: UpdateExceptionListItemOptions): Promise<ExceptionListSchema | null> => {
|
||||
}: UpdateExceptionListItemOptions): Promise<ExceptionListItemSchema | null> => {
|
||||
const { savedObjectsClient, user } = this;
|
||||
return updateExceptionListItem({
|
||||
_tags,
|
||||
|
@ -196,7 +197,7 @@ export class ExceptionListClient {
|
|||
id,
|
||||
itemId,
|
||||
namespaceType,
|
||||
}: DeleteExceptionListItemOptions): Promise<ExceptionListSchema | null> => {
|
||||
}: DeleteExceptionListItemOptions): Promise<ExceptionListItemSchema | null> => {
|
||||
const { savedObjectsClient } = this;
|
||||
return deleteExceptionListItem({
|
||||
id,
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
EntriesArrayOrUndefined,
|
||||
ExceptionListType,
|
||||
ExceptionListTypeOrUndefined,
|
||||
FilterOrUndefined,
|
||||
IdOrUndefined,
|
||||
ItemId,
|
||||
ItemIdOrUndefined,
|
||||
|
@ -22,14 +23,17 @@ import {
|
|||
MetaOrUndefined,
|
||||
Name,
|
||||
NameOrUndefined,
|
||||
NamespaceType,
|
||||
PageOrUndefined,
|
||||
PerPageOrUndefined,
|
||||
SortFieldOrUndefined,
|
||||
SortOrderOrUndefined,
|
||||
Tags,
|
||||
TagsOrUndefined,
|
||||
_Tags,
|
||||
_TagsOrUndefined,
|
||||
} from '../../../common/schemas';
|
||||
|
||||
import { NamespaceType } from './types';
|
||||
|
||||
export interface ConstructorOptions {
|
||||
user: string;
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
|
@ -113,18 +117,18 @@ export interface UpdateExceptionListItemOptions {
|
|||
export interface FindExceptionListItemOptions {
|
||||
listId: ListId;
|
||||
namespaceType: NamespaceType;
|
||||
filter: string | undefined;
|
||||
perPage: number | undefined;
|
||||
page: number | undefined;
|
||||
sortField: string | undefined;
|
||||
sortOrder: string | undefined;
|
||||
filter: FilterOrUndefined;
|
||||
perPage: PerPageOrUndefined;
|
||||
page: PageOrUndefined;
|
||||
sortField: SortFieldOrUndefined;
|
||||
sortOrder: SortOrderOrUndefined;
|
||||
}
|
||||
|
||||
export interface FindExceptionListOptions {
|
||||
namespaceType: NamespaceType;
|
||||
filter: string | undefined;
|
||||
perPage: number | undefined;
|
||||
page: number | undefined;
|
||||
sortField: string | undefined;
|
||||
sortOrder: string | undefined;
|
||||
filter: FilterOrUndefined;
|
||||
perPage: PerPageOrUndefined;
|
||||
page: PageOrUndefined;
|
||||
sortField: SortFieldOrUndefined;
|
||||
sortOrder: SortOrderOrUndefined;
|
||||
}
|
||||
|
|
|
@ -6,20 +6,28 @@
|
|||
|
||||
import { SavedObjectsClientContract } from 'kibana/server';
|
||||
|
||||
import { ExceptionListSoSchema, FoundExceptionListSchema } from '../../../common/schemas';
|
||||
import {
|
||||
ExceptionListSoSchema,
|
||||
FilterOrUndefined,
|
||||
FoundExceptionListSchema,
|
||||
NamespaceType,
|
||||
PageOrUndefined,
|
||||
PerPageOrUndefined,
|
||||
SortFieldOrUndefined,
|
||||
SortOrderOrUndefined,
|
||||
} from '../../../common/schemas';
|
||||
import { SavedObjectType } from '../../saved_objects';
|
||||
|
||||
import { getSavedObjectType, transformSavedObjectsToFounExceptionList } from './utils';
|
||||
import { NamespaceType } from './types';
|
||||
|
||||
interface FindExceptionListOptions {
|
||||
namespaceType: NamespaceType;
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
filter: string | undefined;
|
||||
perPage: number | undefined;
|
||||
page: number | undefined;
|
||||
sortField: string | undefined;
|
||||
sortOrder: string | undefined;
|
||||
filter: FilterOrUndefined;
|
||||
perPage: PerPageOrUndefined;
|
||||
page: PageOrUndefined;
|
||||
sortField: SortFieldOrUndefined;
|
||||
sortOrder: SortOrderOrUndefined;
|
||||
}
|
||||
|
||||
export const findExceptionList = async ({
|
||||
|
@ -40,14 +48,14 @@ export const findExceptionList = async ({
|
|||
sortOrder,
|
||||
type: savedObjectType,
|
||||
});
|
||||
return transformSavedObjectsToFounExceptionList({ savedObjectsFindResponse });
|
||||
return transformSavedObjectsToFounExceptionList({ namespaceType, savedObjectsFindResponse });
|
||||
};
|
||||
|
||||
export const getExceptionListFilter = ({
|
||||
filter,
|
||||
savedObjectType,
|
||||
}: {
|
||||
filter: string | undefined;
|
||||
filter: FilterOrUndefined;
|
||||
savedObjectType: SavedObjectType;
|
||||
}): string => {
|
||||
if (filter == null) {
|
||||
|
|
|
@ -8,24 +8,29 @@ import { SavedObjectsClientContract } from 'kibana/server';
|
|||
|
||||
import {
|
||||
ExceptionListSoSchema,
|
||||
FilterOrUndefined,
|
||||
FoundExceptionListItemSchema,
|
||||
ListId,
|
||||
NamespaceType,
|
||||
PageOrUndefined,
|
||||
PerPageOrUndefined,
|
||||
SortFieldOrUndefined,
|
||||
SortOrderOrUndefined,
|
||||
} from '../../../common/schemas';
|
||||
import { SavedObjectType } from '../../saved_objects';
|
||||
|
||||
import { getSavedObjectType, transformSavedObjectsToFounExceptionListItem } from './utils';
|
||||
import { NamespaceType } from './types';
|
||||
import { getExceptionList } from './get_exception_list';
|
||||
|
||||
interface FindExceptionListItemOptions {
|
||||
listId: ListId;
|
||||
namespaceType: NamespaceType;
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
filter: string | undefined;
|
||||
perPage: number | undefined;
|
||||
page: number | undefined;
|
||||
sortField: string | undefined;
|
||||
sortOrder: string | undefined;
|
||||
filter: FilterOrUndefined;
|
||||
perPage: PerPageOrUndefined;
|
||||
page: PageOrUndefined;
|
||||
sortField: SortFieldOrUndefined;
|
||||
sortOrder: SortOrderOrUndefined;
|
||||
}
|
||||
|
||||
export const findExceptionListItem = async ({
|
||||
|
@ -56,7 +61,10 @@ export const findExceptionListItem = async ({
|
|||
sortOrder,
|
||||
type: savedObjectType,
|
||||
});
|
||||
return transformSavedObjectsToFounExceptionListItem({ savedObjectsFindResponse });
|
||||
return transformSavedObjectsToFounExceptionListItem({
|
||||
namespaceType,
|
||||
savedObjectsFindResponse,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -66,7 +74,7 @@ export const getExceptionListItemFilter = ({
|
|||
savedObjectType,
|
||||
}: {
|
||||
listId: ListId;
|
||||
filter: string | undefined;
|
||||
filter: FilterOrUndefined;
|
||||
savedObjectType: SavedObjectType;
|
||||
}): string => {
|
||||
if (filter == null) {
|
||||
|
|
|
@ -13,10 +13,10 @@ import {
|
|||
ExceptionListSoSchema,
|
||||
IdOrUndefined,
|
||||
ListIdOrUndefined,
|
||||
NamespaceType,
|
||||
} from '../../../common/schemas';
|
||||
|
||||
import { getSavedObjectType, transformSavedObjectToExceptionList } from './utils';
|
||||
import { NamespaceType } from './types';
|
||||
|
||||
interface GetExceptionListOptions {
|
||||
id: IdOrUndefined;
|
||||
|
@ -35,7 +35,7 @@ export const getExceptionList = async ({
|
|||
if (id != null) {
|
||||
try {
|
||||
const savedObject = await savedObjectsClient.get<ExceptionListSoSchema>(savedObjectType, id);
|
||||
return transformSavedObjectToExceptionList({ savedObject });
|
||||
return transformSavedObjectToExceptionList({ namespaceType, savedObject });
|
||||
} catch (err) {
|
||||
if (SavedObjectsErrorHelpers.isNotFoundError(err)) {
|
||||
return null;
|
||||
|
@ -54,7 +54,10 @@ export const getExceptionList = async ({
|
|||
type: savedObjectType,
|
||||
});
|
||||
if (savedObject.saved_objects[0] != null) {
|
||||
return transformSavedObjectToExceptionList({ savedObject: savedObject.saved_objects[0] });
|
||||
return transformSavedObjectToExceptionList({
|
||||
namespaceType,
|
||||
savedObject: savedObject.saved_objects[0],
|
||||
});
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -13,10 +13,10 @@ import {
|
|||
ExceptionListSoSchema,
|
||||
IdOrUndefined,
|
||||
ItemIdOrUndefined,
|
||||
NamespaceType,
|
||||
} from '../../../common/schemas';
|
||||
|
||||
import { getSavedObjectType, transformSavedObjectToExceptionListItem } from './utils';
|
||||
import { NamespaceType } from './types';
|
||||
|
||||
interface GetExceptionListItemOptions {
|
||||
id: IdOrUndefined;
|
||||
|
@ -35,7 +35,7 @@ export const getExceptionListItem = async ({
|
|||
if (id != null) {
|
||||
try {
|
||||
const savedObject = await savedObjectsClient.get<ExceptionListSoSchema>(savedObjectType, id);
|
||||
return transformSavedObjectToExceptionListItem({ savedObject });
|
||||
return transformSavedObjectToExceptionListItem({ namespaceType, savedObject });
|
||||
} catch (err) {
|
||||
if (SavedObjectsErrorHelpers.isNotFoundError(err)) {
|
||||
return null;
|
||||
|
@ -55,6 +55,7 @@ export const getExceptionListItem = async ({
|
|||
});
|
||||
if (savedObject.saved_objects[0] != null) {
|
||||
return transformSavedObjectToExceptionListItem({
|
||||
namespaceType,
|
||||
savedObject: savedObject.saved_objects[0],
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -15,12 +15,12 @@ import {
|
|||
ListIdOrUndefined,
|
||||
MetaOrUndefined,
|
||||
NameOrUndefined,
|
||||
NamespaceType,
|
||||
TagsOrUndefined,
|
||||
_TagsOrUndefined,
|
||||
} from '../../../common/schemas';
|
||||
|
||||
import { getSavedObjectType, transformSavedObjectUpdateToExceptionList } from './utils';
|
||||
import { NamespaceType } from './types';
|
||||
import { getExceptionList } from './get_exception_list';
|
||||
|
||||
interface UpdateExceptionListOptions {
|
||||
|
@ -69,6 +69,6 @@ export const updateExceptionList = async ({
|
|||
updated_by: user,
|
||||
}
|
||||
);
|
||||
return transformSavedObjectUpdateToExceptionList({ exceptionList, savedObject });
|
||||
return transformSavedObjectUpdateToExceptionList({ exceptionList, namespaceType, savedObject });
|
||||
}
|
||||
};
|
||||
|
|
|
@ -17,12 +17,12 @@ import {
|
|||
ItemIdOrUndefined,
|
||||
MetaOrUndefined,
|
||||
NameOrUndefined,
|
||||
NamespaceType,
|
||||
TagsOrUndefined,
|
||||
_TagsOrUndefined,
|
||||
} from '../../../common/schemas';
|
||||
|
||||
import { getSavedObjectType, transformSavedObjectUpdateToExceptionListItem } from './utils';
|
||||
import { NamespaceType } from './types';
|
||||
import { getExceptionListItem } from './get_exception_list_item';
|
||||
|
||||
interface UpdateExceptionListItemOptions {
|
||||
|
@ -82,6 +82,10 @@ export const updateExceptionListItem = async ({
|
|||
updated_by: user,
|
||||
}
|
||||
);
|
||||
return transformSavedObjectUpdateToExceptionListItem({ exceptionListItem, savedObject });
|
||||
return transformSavedObjectUpdateToExceptionListItem({
|
||||
exceptionListItem,
|
||||
namespaceType,
|
||||
savedObject,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
ExceptionListSoSchema,
|
||||
FoundExceptionListItemSchema,
|
||||
FoundExceptionListSchema,
|
||||
NamespaceType,
|
||||
} from '../../../common/schemas';
|
||||
import {
|
||||
SavedObjectType,
|
||||
|
@ -19,8 +20,6 @@ import {
|
|||
exceptionListSavedObjectType,
|
||||
} from '../../saved_objects';
|
||||
|
||||
import { NamespaceType } from './types';
|
||||
|
||||
export const getSavedObjectType = ({
|
||||
namespaceType,
|
||||
}: {
|
||||
|
@ -35,8 +34,10 @@ export const getSavedObjectType = ({
|
|||
|
||||
export const transformSavedObjectToExceptionList = ({
|
||||
savedObject,
|
||||
namespaceType,
|
||||
}: {
|
||||
savedObject: SavedObject<ExceptionListSoSchema>;
|
||||
namespaceType: NamespaceType;
|
||||
}): ExceptionListSchema => {
|
||||
const dateNow = new Date().toISOString();
|
||||
const {
|
||||
|
@ -68,6 +69,7 @@ export const transformSavedObjectToExceptionList = ({
|
|||
list_id,
|
||||
meta,
|
||||
name,
|
||||
namespace_type: namespaceType,
|
||||
tags,
|
||||
tie_breaker_id,
|
||||
type,
|
||||
|
@ -79,9 +81,11 @@ export const transformSavedObjectToExceptionList = ({
|
|||
export const transformSavedObjectUpdateToExceptionList = ({
|
||||
exceptionList,
|
||||
savedObject,
|
||||
namespaceType,
|
||||
}: {
|
||||
exceptionList: ExceptionListSchema;
|
||||
savedObject: SavedObjectsUpdateResponse<ExceptionListSoSchema>;
|
||||
namespaceType: NamespaceType;
|
||||
}): ExceptionListSchema => {
|
||||
const dateNow = new Date().toISOString();
|
||||
const {
|
||||
|
@ -101,6 +105,7 @@ export const transformSavedObjectUpdateToExceptionList = ({
|
|||
list_id: exceptionList.list_id,
|
||||
meta: meta ?? exceptionList.meta,
|
||||
name: name ?? exceptionList.name,
|
||||
namespace_type: namespaceType,
|
||||
tags: tags ?? exceptionList.tags,
|
||||
tie_breaker_id: exceptionList.tie_breaker_id,
|
||||
type: type ?? exceptionList.type,
|
||||
|
@ -111,8 +116,10 @@ export const transformSavedObjectUpdateToExceptionList = ({
|
|||
|
||||
export const transformSavedObjectToExceptionListItem = ({
|
||||
savedObject,
|
||||
namespaceType,
|
||||
}: {
|
||||
savedObject: SavedObject<ExceptionListSoSchema>;
|
||||
namespaceType: NamespaceType;
|
||||
}): ExceptionListItemSchema => {
|
||||
const dateNow = new Date().toISOString();
|
||||
const {
|
||||
|
@ -150,6 +157,7 @@ export const transformSavedObjectToExceptionListItem = ({
|
|||
list_id,
|
||||
meta,
|
||||
name,
|
||||
namespace_type: namespaceType,
|
||||
tags,
|
||||
tie_breaker_id,
|
||||
type,
|
||||
|
@ -161,9 +169,11 @@ export const transformSavedObjectToExceptionListItem = ({
|
|||
export const transformSavedObjectUpdateToExceptionListItem = ({
|
||||
exceptionListItem,
|
||||
savedObject,
|
||||
namespaceType,
|
||||
}: {
|
||||
exceptionListItem: ExceptionListItemSchema;
|
||||
savedObject: SavedObjectsUpdateResponse<ExceptionListSoSchema>;
|
||||
namespaceType: NamespaceType;
|
||||
}): ExceptionListItemSchema => {
|
||||
const dateNow = new Date().toISOString();
|
||||
const {
|
||||
|
@ -196,6 +206,7 @@ export const transformSavedObjectUpdateToExceptionListItem = ({
|
|||
list_id: exceptionListItem.list_id,
|
||||
meta: meta ?? exceptionListItem.meta,
|
||||
name: name ?? exceptionListItem.name,
|
||||
namespace_type: namespaceType,
|
||||
tags: tags ?? exceptionListItem.tags,
|
||||
tie_breaker_id: exceptionListItem.tie_breaker_id,
|
||||
type: type ?? exceptionListItem.type,
|
||||
|
@ -206,12 +217,14 @@ export const transformSavedObjectUpdateToExceptionListItem = ({
|
|||
|
||||
export const transformSavedObjectsToFounExceptionListItem = ({
|
||||
savedObjectsFindResponse,
|
||||
namespaceType,
|
||||
}: {
|
||||
savedObjectsFindResponse: SavedObjectsFindResponse<ExceptionListSoSchema>;
|
||||
namespaceType: NamespaceType;
|
||||
}): FoundExceptionListItemSchema => {
|
||||
return {
|
||||
data: savedObjectsFindResponse.saved_objects.map((savedObject) =>
|
||||
transformSavedObjectToExceptionListItem({ savedObject })
|
||||
transformSavedObjectToExceptionListItem({ namespaceType, savedObject })
|
||||
),
|
||||
page: savedObjectsFindResponse.page,
|
||||
per_page: savedObjectsFindResponse.per_page,
|
||||
|
@ -221,12 +234,14 @@ export const transformSavedObjectsToFounExceptionListItem = ({
|
|||
|
||||
export const transformSavedObjectsToFounExceptionList = ({
|
||||
savedObjectsFindResponse,
|
||||
namespaceType,
|
||||
}: {
|
||||
savedObjectsFindResponse: SavedObjectsFindResponse<ExceptionListSoSchema>;
|
||||
namespaceType: NamespaceType;
|
||||
}): FoundExceptionListSchema => {
|
||||
return {
|
||||
data: savedObjectsFindResponse.saved_objects.map((savedObject) =>
|
||||
transformSavedObjectToExceptionList({ savedObject })
|
||||
transformSavedObjectToExceptionList({ namespaceType, savedObject })
|
||||
),
|
||||
page: savedObjectsFindResponse.page,
|
||||
per_page: savedObjectsFindResponse.per_page,
|
||||
|
|
|
@ -58,7 +58,7 @@ export const createListItem = async ({
|
|||
...transformListItemToElasticQuery({ type, value }),
|
||||
};
|
||||
|
||||
const response: CreateDocumentResponse = await callCluster('index', {
|
||||
const response = await callCluster<CreateDocumentResponse>('index', {
|
||||
body,
|
||||
id,
|
||||
index: listItemIndex,
|
||||
|
|
116
x-pack/plugins/lists/server/services/items/find_list_item.ts
Normal file
116
x-pack/plugins/lists/server/services/items/find_list_item.ts
Normal file
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { APICaller } from 'kibana/server';
|
||||
|
||||
import {
|
||||
Filter,
|
||||
FoundListItemSchema,
|
||||
ListId,
|
||||
Page,
|
||||
PerPage,
|
||||
SearchEsListItemSchema,
|
||||
SortFieldOrUndefined,
|
||||
SortOrderOrUndefined,
|
||||
} from '../../../common/schemas';
|
||||
import { getList } from '../lists';
|
||||
import {
|
||||
encodeCursor,
|
||||
getQueryFilter,
|
||||
getSearchAfterWithTieBreaker,
|
||||
getSortWithTieBreaker,
|
||||
scrollToStartPage,
|
||||
transformElasticToListItem,
|
||||
} from '../utils';
|
||||
|
||||
interface FindListItemOptions {
|
||||
listId: ListId;
|
||||
filter: Filter;
|
||||
currentIndexPosition: number;
|
||||
searchAfter: string[] | undefined;
|
||||
perPage: PerPage;
|
||||
page: Page;
|
||||
sortField: SortFieldOrUndefined;
|
||||
sortOrder: SortOrderOrUndefined;
|
||||
callCluster: APICaller;
|
||||
listIndex: string;
|
||||
listItemIndex: string;
|
||||
}
|
||||
|
||||
export const findListItem = async ({
|
||||
callCluster,
|
||||
currentIndexPosition,
|
||||
filter,
|
||||
listId,
|
||||
page,
|
||||
perPage,
|
||||
searchAfter,
|
||||
sortField: sortFieldWithPossibleValue,
|
||||
listIndex,
|
||||
listItemIndex,
|
||||
sortOrder,
|
||||
}: FindListItemOptions): Promise<FoundListItemSchema | null> => {
|
||||
const query = getQueryFilter({ filter });
|
||||
const list = await getList({ callCluster, id: listId, listIndex });
|
||||
if (list == null) {
|
||||
return null;
|
||||
} else {
|
||||
const sortField =
|
||||
sortFieldWithPossibleValue === 'value' ? list.type : sortFieldWithPossibleValue;
|
||||
const scroll = await scrollToStartPage({
|
||||
callCluster,
|
||||
currentIndexPosition,
|
||||
filter,
|
||||
hopSize: 100,
|
||||
index: listItemIndex,
|
||||
page,
|
||||
perPage,
|
||||
searchAfter,
|
||||
sortField,
|
||||
sortOrder,
|
||||
});
|
||||
|
||||
const { count } = await callCluster('count', {
|
||||
body: {
|
||||
query,
|
||||
},
|
||||
ignoreUnavailable: true,
|
||||
index: listItemIndex,
|
||||
});
|
||||
|
||||
if (scroll.validSearchAfterFound) {
|
||||
const response = await callCluster<SearchEsListItemSchema>('search', {
|
||||
body: {
|
||||
query,
|
||||
search_after: scroll.searchAfter,
|
||||
sort: getSortWithTieBreaker({ sortField, sortOrder }),
|
||||
},
|
||||
ignoreUnavailable: true,
|
||||
index: listItemIndex,
|
||||
size: perPage,
|
||||
});
|
||||
return {
|
||||
cursor: encodeCursor({
|
||||
page,
|
||||
perPage,
|
||||
searchAfter: getSearchAfterWithTieBreaker({ response, sortField }),
|
||||
}),
|
||||
data: transformElasticToListItem({ response, type: list.type }),
|
||||
page,
|
||||
per_page: perPage,
|
||||
total: count,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
cursor: encodeCursor({ page, perPage, searchAfter: undefined }),
|
||||
data: [],
|
||||
page,
|
||||
per_page: perPage,
|
||||
total: count,
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
|
@ -4,7 +4,6 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { SearchResponse } from 'elasticsearch';
|
||||
import { APICaller } from 'kibana/server';
|
||||
|
||||
import { Id, ListItemSchema, SearchEsListItemSchema } from '../../../common/schemas';
|
||||
|
@ -21,7 +20,7 @@ export const getListItem = async ({
|
|||
callCluster,
|
||||
listItemIndex,
|
||||
}: GetListItemOptions): Promise<ListItemSchema | null> => {
|
||||
const listItemES: SearchResponse<SearchEsListItemSchema> = await callCluster('search', {
|
||||
const listItemES = await callCluster<SearchEsListItemSchema>('search', {
|
||||
body: {
|
||||
query: {
|
||||
term: {
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { SearchResponse } from 'elasticsearch';
|
||||
import { APICaller } from 'kibana/server';
|
||||
|
||||
import { ListItemArraySchema, SearchEsListItemSchema, Type } from '../../../common/schemas';
|
||||
|
@ -25,7 +24,7 @@ export const getListItemByValues = async ({
|
|||
type,
|
||||
value,
|
||||
}: GetListItemByValuesOptions): Promise<ListItemArraySchema> => {
|
||||
const response: SearchResponse<SearchEsListItemSchema> = await callCluster('search', {
|
||||
const response = await callCluster<SearchEsListItemSchema>('search', {
|
||||
body: {
|
||||
query: {
|
||||
bool: {
|
||||
|
|
|
@ -8,12 +8,13 @@ export * from './buffer_lines';
|
|||
export * from './create_list_item';
|
||||
export * from './create_list_items_bulk';
|
||||
export * from './delete_list_item_by_value';
|
||||
export * from './delete_list_item';
|
||||
export * from './find_list_item';
|
||||
export * from './get_list_item_by_value';
|
||||
export * from './get_list_item';
|
||||
export * from './get_list_item_by_values';
|
||||
export * from './get_list_item_template';
|
||||
export * from './get_list_item_index';
|
||||
export * from './update_list_item';
|
||||
export * from './write_lines_to_bulk_list_items';
|
||||
export * from './write_list_items_to_stream';
|
||||
export * from './get_list_item_template';
|
||||
export * from './delete_list_item';
|
||||
export * from './get_list_item_index';
|
||||
|
|
|
@ -48,7 +48,7 @@ export const updateListItem = async ({
|
|||
...transformListItemToElasticQuery({ type: listItem.type, value: value ?? listItem.value }),
|
||||
};
|
||||
|
||||
const response: CreateDocumentResponse = await callCluster('update', {
|
||||
const response = await callCluster<CreateDocumentResponse>('update', {
|
||||
body: {
|
||||
doc,
|
||||
},
|
||||
|
|
|
@ -114,7 +114,7 @@ export const getResponse = async ({
|
|||
listItemIndex,
|
||||
size = SIZE,
|
||||
}: GetResponseOptions): Promise<SearchResponse<SearchEsListItemSchema>> => {
|
||||
return callCluster('search', {
|
||||
return callCluster<SearchEsListItemSchema>('search', {
|
||||
body: {
|
||||
query: {
|
||||
term: {
|
||||
|
|
|
@ -55,7 +55,7 @@ export const createList = async ({
|
|||
updated_at: createdAt,
|
||||
updated_by: user,
|
||||
};
|
||||
const response: CreateDocumentResponse = await callCluster('index', {
|
||||
const response = await callCluster<CreateDocumentResponse>('index', {
|
||||
body,
|
||||
id,
|
||||
index: listIndex,
|
||||
|
|
104
x-pack/plugins/lists/server/services/lists/find_list.ts
Normal file
104
x-pack/plugins/lists/server/services/lists/find_list.ts
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { APICaller } from 'kibana/server';
|
||||
|
||||
import {
|
||||
Filter,
|
||||
FoundListSchema,
|
||||
Page,
|
||||
PerPage,
|
||||
SearchEsListSchema,
|
||||
SortFieldOrUndefined,
|
||||
SortOrderOrUndefined,
|
||||
} from '../../../common/schemas';
|
||||
import {
|
||||
encodeCursor,
|
||||
getQueryFilter,
|
||||
getSearchAfterWithTieBreaker,
|
||||
getSortWithTieBreaker,
|
||||
scrollToStartPage,
|
||||
transformElasticToList,
|
||||
} from '../utils';
|
||||
|
||||
interface FindListOptions {
|
||||
filter: Filter;
|
||||
currentIndexPosition: number;
|
||||
searchAfter: string[] | undefined;
|
||||
perPage: PerPage;
|
||||
page: Page;
|
||||
sortField: SortFieldOrUndefined;
|
||||
sortOrder: SortOrderOrUndefined;
|
||||
callCluster: APICaller;
|
||||
listIndex: string;
|
||||
}
|
||||
|
||||
export const findList = async ({
|
||||
callCluster,
|
||||
currentIndexPosition,
|
||||
filter,
|
||||
page,
|
||||
perPage,
|
||||
searchAfter,
|
||||
sortField,
|
||||
listIndex,
|
||||
sortOrder,
|
||||
}: FindListOptions): Promise<FoundListSchema> => {
|
||||
const query = getQueryFilter({ filter });
|
||||
|
||||
const scroll = await scrollToStartPage({
|
||||
callCluster,
|
||||
currentIndexPosition,
|
||||
filter,
|
||||
hopSize: 100,
|
||||
index: listIndex,
|
||||
page,
|
||||
perPage,
|
||||
searchAfter,
|
||||
sortField,
|
||||
sortOrder,
|
||||
});
|
||||
|
||||
const { count } = await callCluster('count', {
|
||||
body: {
|
||||
query,
|
||||
},
|
||||
ignoreUnavailable: true,
|
||||
index: listIndex,
|
||||
});
|
||||
|
||||
if (scroll.validSearchAfterFound) {
|
||||
const response = await callCluster<SearchEsListSchema>('search', {
|
||||
body: {
|
||||
query,
|
||||
search_after: scroll.searchAfter,
|
||||
sort: getSortWithTieBreaker({ sortField, sortOrder }),
|
||||
},
|
||||
ignoreUnavailable: true,
|
||||
index: listIndex,
|
||||
size: perPage,
|
||||
});
|
||||
return {
|
||||
cursor: encodeCursor({
|
||||
page,
|
||||
perPage,
|
||||
searchAfter: getSearchAfterWithTieBreaker({ response, sortField }),
|
||||
}),
|
||||
data: transformElasticToList({ response }),
|
||||
page,
|
||||
per_page: perPage,
|
||||
total: count,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
cursor: encodeCursor({ page, perPage, searchAfter: undefined }),
|
||||
data: [],
|
||||
page,
|
||||
per_page: perPage,
|
||||
total: count,
|
||||
};
|
||||
}
|
||||
};
|
|
@ -4,10 +4,10 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { SearchResponse } from 'elasticsearch';
|
||||
import { APICaller } from 'kibana/server';
|
||||
|
||||
import { Id, ListSchema, SearchEsListSchema } from '../../../common/schemas';
|
||||
import { transformElasticToList } from '../utils/transform_elastic_to_list';
|
||||
|
||||
interface GetListOptions {
|
||||
id: Id;
|
||||
|
@ -20,7 +20,7 @@ export const getList = async ({
|
|||
callCluster,
|
||||
listIndex,
|
||||
}: GetListOptions): Promise<ListSchema | null> => {
|
||||
const result: SearchResponse<SearchEsListSchema> = await callCluster('search', {
|
||||
const response = await callCluster<SearchEsListSchema>('search', {
|
||||
body: {
|
||||
query: {
|
||||
term: {
|
||||
|
@ -31,12 +31,6 @@ export const getList = async ({
|
|||
ignoreUnavailable: true,
|
||||
index: listIndex,
|
||||
});
|
||||
if (result.hits.hits.length) {
|
||||
return {
|
||||
id: result.hits.hits[0]._id,
|
||||
...result.hits.hits[0]._source,
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
const list = transformElasticToList({ response });
|
||||
return list[0] ?? null;
|
||||
};
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
export * from './create_list';
|
||||
export * from './delete_list';
|
||||
export * from './find_list';
|
||||
export * from './get_list';
|
||||
export * from './get_list_template';
|
||||
export * from './update_list';
|
||||
|
|
|
@ -6,11 +6,18 @@
|
|||
|
||||
import { APICaller } from 'kibana/server';
|
||||
|
||||
import { ListItemArraySchema, ListItemSchema, ListSchema } from '../../../common/schemas';
|
||||
import {
|
||||
FoundListItemSchema,
|
||||
FoundListSchema,
|
||||
ListItemArraySchema,
|
||||
ListItemSchema,
|
||||
ListSchema,
|
||||
} from '../../../common/schemas';
|
||||
import { ConfigType } from '../../config';
|
||||
import {
|
||||
createList,
|
||||
deleteList,
|
||||
findList,
|
||||
getList,
|
||||
getListIndex,
|
||||
getListTemplate,
|
||||
|
@ -21,6 +28,7 @@ import {
|
|||
deleteListItem,
|
||||
deleteListItemByValue,
|
||||
exportListItemsToStream,
|
||||
findListItem,
|
||||
getListItem,
|
||||
getListItemByValue,
|
||||
getListItemByValues,
|
||||
|
@ -52,6 +60,8 @@ import {
|
|||
DeleteListItemOptions,
|
||||
DeleteListOptions,
|
||||
ExportListItemsToStreamOptions,
|
||||
FindListItemOptions,
|
||||
FindListOptions,
|
||||
GetListItemByValueOptions,
|
||||
GetListItemOptions,
|
||||
GetListItemsByValueOptions,
|
||||
|
@ -410,4 +420,56 @@ export class ListClient {
|
|||
value,
|
||||
});
|
||||
};
|
||||
|
||||
public findList = async ({
|
||||
filter,
|
||||
currentIndexPosition,
|
||||
perPage,
|
||||
page,
|
||||
sortField,
|
||||
sortOrder,
|
||||
searchAfter,
|
||||
}: FindListOptions): Promise<FoundListSchema> => {
|
||||
const { callCluster } = this;
|
||||
const listIndex = this.getListIndex();
|
||||
return findList({
|
||||
callCluster,
|
||||
currentIndexPosition,
|
||||
filter,
|
||||
listIndex,
|
||||
page,
|
||||
perPage,
|
||||
searchAfter,
|
||||
sortField,
|
||||
sortOrder,
|
||||
});
|
||||
};
|
||||
|
||||
public findListItem = async ({
|
||||
listId,
|
||||
filter,
|
||||
currentIndexPosition,
|
||||
perPage,
|
||||
page,
|
||||
sortField,
|
||||
sortOrder,
|
||||
searchAfter,
|
||||
}: FindListItemOptions): Promise<FoundListItemSchema | null> => {
|
||||
const { callCluster } = this;
|
||||
const listIndex = this.getListIndex();
|
||||
const listItemIndex = this.getListItemIndex();
|
||||
return findListItem({
|
||||
callCluster,
|
||||
currentIndexPosition,
|
||||
filter,
|
||||
listId,
|
||||
listIndex,
|
||||
listItemIndex,
|
||||
page,
|
||||
perPage,
|
||||
searchAfter,
|
||||
sortField,
|
||||
sortOrder,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -11,11 +11,17 @@ import { APICaller } from 'kibana/server';
|
|||
import {
|
||||
Description,
|
||||
DescriptionOrUndefined,
|
||||
Filter,
|
||||
Id,
|
||||
IdOrUndefined,
|
||||
ListId,
|
||||
MetaOrUndefined,
|
||||
Name,
|
||||
NameOrUndefined,
|
||||
Page,
|
||||
PerPage,
|
||||
SortFieldOrUndefined,
|
||||
SortOrderOrUndefined,
|
||||
Type,
|
||||
} from '../../../common/schemas';
|
||||
import { ConfigType } from '../../config';
|
||||
|
@ -110,3 +116,24 @@ export interface GetListItemsByValueOptions {
|
|||
listId: string;
|
||||
value: string[];
|
||||
}
|
||||
|
||||
export interface FindListOptions {
|
||||
currentIndexPosition: number;
|
||||
filter: Filter;
|
||||
perPage: PerPage;
|
||||
page: Page;
|
||||
searchAfter: string[] | undefined;
|
||||
sortField: SortFieldOrUndefined;
|
||||
sortOrder: SortOrderOrUndefined;
|
||||
}
|
||||
|
||||
export interface FindListItemOptions {
|
||||
currentIndexPosition: number;
|
||||
filter: Filter;
|
||||
listId: ListId;
|
||||
perPage: PerPage;
|
||||
page: Page;
|
||||
searchAfter: string[] | undefined;
|
||||
sortField: SortFieldOrUndefined;
|
||||
sortOrder: SortOrderOrUndefined;
|
||||
}
|
||||
|
|
|
@ -3,4 +3,8 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
export type NamespaceType = 'agnostic' | 'single';
|
||||
|
||||
interface Scroll {
|
||||
searchAfter: string[] | undefined;
|
||||
validSearchAfterFound: boolean;
|
||||
}
|
|
@ -51,7 +51,7 @@ export const updateList = async ({
|
|||
updated_at: updatedAt,
|
||||
updated_by: user,
|
||||
};
|
||||
const response: CreateDocumentResponse = await callCluster('update', {
|
||||
const response = await callCluster<CreateDocumentResponse>('update', {
|
||||
body: { doc },
|
||||
id,
|
||||
index: listIndex,
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { Page, PerPage } from '../../../common/schemas';
|
||||
|
||||
interface CalculateScrollMathOptions {
|
||||
perPage: PerPage;
|
||||
page: Page;
|
||||
hopSize: number;
|
||||
currentIndexPosition: number;
|
||||
}
|
||||
|
||||
interface CalculateScrollMathReturn {
|
||||
hops: number;
|
||||
leftOverAfterHops: number;
|
||||
}
|
||||
|
||||
export const calculateScrollMath = ({
|
||||
currentIndexPosition,
|
||||
page,
|
||||
perPage,
|
||||
hopSize,
|
||||
}: CalculateScrollMathOptions): CalculateScrollMathReturn => {
|
||||
const startPageIndex = (page - 1) * perPage - currentIndexPosition;
|
||||
if (startPageIndex < 0) {
|
||||
// This should never be hit but just in case I do a check. We do validate higher above this
|
||||
// before the current index position gets to this point but to be safe we add this line.
|
||||
throw new Error(
|
||||
`page: ${page}, perPage ${perPage} and currentIndex ${currentIndexPosition} are less than zero`
|
||||
);
|
||||
}
|
||||
const hops = Math.floor(startPageIndex / hopSize);
|
||||
const leftOverAfterHops = startPageIndex - hops * hopSize;
|
||||
return {
|
||||
hops,
|
||||
leftOverAfterHops,
|
||||
};
|
||||
};
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import * as t from 'io-ts';
|
||||
import { fold } from 'fp-ts/lib/Either';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
|
||||
import { CursorOrUndefined, SortFieldOrUndefined } from '../../../common/schemas';
|
||||
import { exactCheck } from '../../../common/siem_common_deps';
|
||||
|
||||
/**
|
||||
* Used only internally for this ad-hoc opaque cursor structure to keep track of the
|
||||
* current page_index that the search_after is currently on. The format of an array
|
||||
* is to be consistent with other compact forms of opaque nature such as a saved object versioning.
|
||||
*
|
||||
* The format is [index of item, search_after_array]
|
||||
*/
|
||||
|
||||
// TODO: Use PositiveInteger from siem once that type is outside of server and in common
|
||||
export const contextCursor = t.tuple([t.number, t.union([t.array(t.string), t.undefined])]);
|
||||
|
||||
export type ContextCursor = t.TypeOf<typeof contextCursor>;
|
||||
|
||||
export interface EncodeCursorOptions {
|
||||
searchAfter: string[] | undefined;
|
||||
page: number;
|
||||
perPage: number;
|
||||
}
|
||||
|
||||
export const encodeCursor = ({ searchAfter, page, perPage }: EncodeCursorOptions): string => {
|
||||
const index = searchAfter != null ? page * perPage : 0;
|
||||
const encodedCursor = searchAfter != null ? [index, searchAfter] : [index];
|
||||
const scrollStringed = JSON.stringify(encodedCursor);
|
||||
return Buffer.from(scrollStringed).toString('base64');
|
||||
};
|
||||
|
||||
export interface DecodeCursorOptions {
|
||||
cursor: CursorOrUndefined;
|
||||
page: number;
|
||||
perPage: number;
|
||||
sortField: SortFieldOrUndefined;
|
||||
}
|
||||
|
||||
export interface DecodeCursor {
|
||||
cursor: ContextCursor;
|
||||
isValid: boolean;
|
||||
errorMessage: string;
|
||||
}
|
||||
|
||||
export const decodeCursor = ({
|
||||
cursor,
|
||||
page,
|
||||
perPage,
|
||||
sortField,
|
||||
}: DecodeCursorOptions): DecodeCursor => {
|
||||
if (cursor == null) {
|
||||
return {
|
||||
cursor: [0, undefined],
|
||||
errorMessage: '',
|
||||
isValid: true,
|
||||
};
|
||||
} else {
|
||||
const fromBuffer = Buffer.from(cursor, 'base64').toString();
|
||||
const parsed = parseOrUndefined(fromBuffer);
|
||||
if (parsed == null) {
|
||||
return {
|
||||
cursor: [0, undefined],
|
||||
errorMessage: 'Error parsing JSON from base64 encoded cursor',
|
||||
isValid: false,
|
||||
};
|
||||
} else {
|
||||
const decodedCursor = contextCursor.decode(parsed);
|
||||
const checked = exactCheck(parsed, decodedCursor);
|
||||
|
||||
const onLeft = (): ContextCursor | undefined => undefined;
|
||||
const onRight = (schema: ContextCursor): ContextCursor | undefined => schema;
|
||||
const cursorOrUndefined = pipe(checked, fold(onLeft, onRight));
|
||||
|
||||
const startPageIndex = (page - 1) * perPage;
|
||||
if (cursorOrUndefined == null) {
|
||||
return {
|
||||
cursor: [0, undefined],
|
||||
errorMessage: 'Error decoding cursor structure',
|
||||
isValid: false,
|
||||
};
|
||||
} else {
|
||||
const [index, searchAfter] = cursorOrUndefined;
|
||||
if (index < 0) {
|
||||
return {
|
||||
cursor: [0, undefined],
|
||||
errorMessage: 'index of cursor cannot be less 0',
|
||||
isValid: false,
|
||||
};
|
||||
} else if (index > startPageIndex) {
|
||||
return {
|
||||
cursor: [0, undefined],
|
||||
errorMessage: `index: ${index} of cursor cannot be greater than the start page index: ${startPageIndex}`,
|
||||
isValid: false,
|
||||
};
|
||||
} else if (searchAfter != null && searchAfter.length > 1 && sortField == null) {
|
||||
return {
|
||||
cursor: [0, undefined],
|
||||
errorMessage: '',
|
||||
isValid: false,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
cursor: [index, searchAfter != null ? searchAfter : undefined],
|
||||
errorMessage: '',
|
||||
isValid: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const parseOrUndefined = (input: string): ContextCursor | undefined => {
|
||||
try {
|
||||
return JSON.parse(input);
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
}
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue