From f6934d2a23c8ab09aab2c4c7b56ab7933bc95d64 Mon Sep 17 00:00:00 2001 From: David Sanchez Soler Date: Wed, 26 May 2021 10:09:21 +0200 Subject: [PATCH 01/17] Shows event filters card on fleet page --- x-pack/plugins/lists/server/routes/index.ts | 1 + .../lists/server/routes/init_routes.ts | 4 + .../routes/summary_exception_list_route.ts | 68 ++++++++++++ .../exception_lists/exception_list_client.ts | 12 ++ .../exception_list_client_types.ts | 13 +++ .../get_exception_list_summary.ts | 103 +++++++++++++++++ .../server/services/exception_lists/index.ts | 1 + .../common/endpoint/types/index.ts | 10 ++ .../common/experimental_features.ts | 2 +- ...ummary.tsx => exception_items_summary.tsx} | 37 +++--- .../components/fleet_event_filters_card.tsx | 105 ++++++++++++++++++ .../components/fleet_trusted_apps_card.tsx | 29 +++-- .../index.tsx | 4 + 13 files changed, 356 insertions(+), 33 deletions(-) create mode 100644 x-pack/plugins/lists/server/routes/summary_exception_list_route.ts create mode 100644 x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts rename x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/{trusted_app_items_summary.tsx => exception_items_summary.tsx} (57%) create mode 100644 x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx diff --git a/x-pack/plugins/lists/server/routes/index.ts b/x-pack/plugins/lists/server/routes/index.ts index aa6749d179971..f8d4deea344b2 100644 --- a/x-pack/plugins/lists/server/routes/index.ts +++ b/x-pack/plugins/lists/server/routes/index.ts @@ -36,6 +36,7 @@ export * from './read_list_index_route'; export * from './read_list_item_route'; export * from './read_list_route'; export * from './read_privileges_route'; +export * from './summary_exception_list_route'; export * from './update_endpoint_list_item_route'; export * from './update_exception_list_item_route'; export * from './update_exception_list_route'; diff --git a/x-pack/plugins/lists/server/routes/init_routes.ts b/x-pack/plugins/lists/server/routes/init_routes.ts index 430dad953a32f..2511596ca8463 100644 --- a/x-pack/plugins/lists/server/routes/init_routes.ts +++ b/x-pack/plugins/lists/server/routes/init_routes.ts @@ -39,6 +39,7 @@ import { readListItemRoute, readListRoute, readPrivilegesRoute, + summaryExceptionListRoute, updateEndpointListItemRoute, updateExceptionListItemRoute, updateExceptionListRoute, @@ -95,4 +96,7 @@ export const initRoutes = (router: ListsPluginRouter, config: ConfigType): void updateEndpointListItemRoute(router); deleteEndpointListItemRoute(router); findEndpointListItemRoute(router); + + // exception list items summary + summaryExceptionListRoute(router); }; diff --git a/x-pack/plugins/lists/server/routes/summary_exception_list_route.ts b/x-pack/plugins/lists/server/routes/summary_exception_list_route.ts new file mode 100644 index 0000000000000..21f9ca5eeffb5 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/summary_exception_list_route.ts @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { transformError } from '@kbn/securitysolution-es-utils'; +import { + ReadExceptionListSchemaDecoded, + readExceptionListSchema, +} from '@kbn/securitysolution-io-ts-list-types'; +import { EXCEPTION_LIST_URL } from '@kbn/securitysolution-list-constants'; + +import type { ListsPluginRouter } from '../types'; + +import { + buildRouteValidation, + buildSiemResponse, + getErrorMessageExceptionList, + getExceptionListClient, +} from './utils'; + +export const summaryExceptionListRoute = (router: ListsPluginRouter): void => { + router.get( + { + options: { + tags: ['access:lists-summary'], + }, + path: `${EXCEPTION_LIST_URL}/_summary`, + validate: { + query: buildRouteValidation( + readExceptionListSchema + ), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { id, list_id: listId, namespace_type: namespaceType } = request.query; + const exceptionLists = getExceptionListClient(context); + if (id != null || listId != null) { + const exceptionListSummary = await exceptionLists.getExceptionListSummary({ + id, + listId, + namespaceType, + }); + if (exceptionListSummary == null) { + return siemResponse.error({ + body: getErrorMessageExceptionList({ id, listId }), + statusCode: 404, + }); + } else { + return response.ok({ body: exceptionListSummary ?? {} }); + } + } else { + return siemResponse.error({ body: 'id or list_id required', statusCode: 400 }); + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts index 803cd04c1d1b4..87ae890f4c424 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts @@ -31,11 +31,14 @@ import { GetEndpointListItemOptions, GetExceptionListItemOptions, GetExceptionListOptions, + GetExceptionListSummaryOptions, + GetExceptionListSummaryResponse, UpdateEndpointListItemOptions, UpdateExceptionListItemOptions, UpdateExceptionListOptions, } from './exception_list_client_types'; import { getExceptionList } from './get_exception_list'; +import { getExceptionListSummary } from './get_exception_list_summary'; import { createExceptionList } from './create_exception_list'; import { getExceptionListItem } from './get_exception_list_item'; import { createExceptionListItem } from './create_exception_list_item'; @@ -72,6 +75,15 @@ export class ExceptionListClient { return getExceptionList({ id, listId, namespaceType, savedObjectsClient }); }; + public getExceptionListSummary = async ({ + listId, + id, + namespaceType, + }: GetExceptionListSummaryOptions): Promise => { + const { savedObjectsClient } = this; + return getExceptionListSummary({ id, listId, namespaceType, savedObjectsClient }); + }; + public getExceptionListItem = async ({ itemId, id, diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts index cbbf7f1513444..acaae9a8da211 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts @@ -56,6 +56,19 @@ export interface GetExceptionListOptions { namespaceType: NamespaceType; } +export interface GetExceptionListSummaryOptions { + listId: ListIdOrUndefined; + id: IdOrUndefined; + namespaceType: NamespaceType; +} + +export interface GetExceptionListSummaryResponse { + windows: number; + linux: number; + macos: number; + total: number; +} + export interface CreateExceptionListOptions { listId: ListId; namespaceType: NamespaceType; diff --git a/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts b/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts new file mode 100644 index 0000000000000..0557a741e655b --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + FoundExceptionListItemSchema, + IdOrUndefined, + ListIdOrUndefined, + NamespaceType, +} from '@kbn/securitysolution-io-ts-list-types'; +import { getSavedObjectType } from '@kbn/securitysolution-list-utils'; + +import { + SavedObjectsClientContract, + SavedObjectsErrorHelpers, +} from '../../../../../../src/core/server/'; +import { ExceptionListSoSchema } from '../../schemas/saved_objects'; + +import { GetExceptionListSummaryResponse } from './exception_list_client_types'; +import { findExceptionListsItem } from './find_exception_list_items'; + +interface GetExceptionListSummaryOptions { + id: IdOrUndefined; + listId: ListIdOrUndefined; + savedObjectsClient: SavedObjectsClientContract; + namespaceType: NamespaceType; +} + +export const getExceptionListSummary = async ({ + id, + listId, + savedObjectsClient, + namespaceType, +}: GetExceptionListSummaryOptions): Promise => { + const savedObjectType = getSavedObjectType({ namespaceType }); + let finalListId: string[] = listId ? [listId] : []; + if (listId === null && id != null) { + try { + const savedObject = await savedObjectsClient.get(savedObjectType, id); + finalListId = [savedObject.attributes.list_id]; + } catch (err) { + if (SavedObjectsErrorHelpers.isNotFoundError(err)) { + return null; + } else { + throw err; + } + } + } else if (listId != null) { + const savedObject = await savedObjectsClient.find({ + filter: `${savedObjectType}.attributes.list_type: list`, + perPage: 1, + search: listId, + searchFields: ['list_id'], + sortField: 'tie_breaker_id', + sortOrder: 'desc', + type: savedObjectType, + }); + if (savedObject.saved_objects[0] == null) { + return null; + } + } else { + return null; + } + + const summary = { + linux: 0, + macos: 0, + total: 0, + windows: 0, + }; + const perPage = 100; + let paging = true; + let page = 1; + + while (paging) { + const items: FoundExceptionListItemSchema | null = await findExceptionListsItem({ + filter: [], + listId: finalListId, + namespaceType: ['agnostic'], + page, + perPage, + savedObjectsClient, + sortField: undefined, + sortOrder: undefined, + }); + + if (!items) break; + + summary.total = items.total; + + for (const item of items.data) { + summary[item.os_types[0]]++; + } + + paging = (page - 1) * perPage + items.data.length < items.total; + page++; + } + + return summary; +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/index.ts b/x-pack/plugins/lists/server/services/exception_lists/index.ts index 21041b9d5d9bd..e6a6dd7ef8c3c 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/index.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/index.ts @@ -15,5 +15,6 @@ export * from './find_exception_list_item'; export * from './find_exception_list_items'; export * from './get_exception_list'; export * from './get_exception_list_item'; +export * from './get_exception_list_summary'; export * from './update_exception_list'; export * from './update_exception_list_item'; diff --git a/x-pack/plugins/security_solution/common/endpoint/types/index.ts b/x-pack/plugins/security_solution/common/endpoint/types/index.ts index 512adffc70eef..142a57dded4f7 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/index.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/index.ts @@ -1095,3 +1095,13 @@ export interface GetAgentSummaryResponse { versions_count: { [key: string]: number }; }; } + +/** + * REST API response for retrieving exception summary + */ +export interface GetExceptionSummaryResponse { + total: number; + windows: number; + macos: number; + linux: number; +} diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index 8d1cc4ca2c1f0..1f667e337a104 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -14,7 +14,7 @@ export type ExperimentalFeatures = typeof allowedExperimentalValues; const allowedExperimentalValues = Object.freeze({ trustedAppsByPolicyEnabled: false, metricsEntitiesEnabled: false, - eventFilteringEnabled: false, + eventFilteringEnabled: true, hostIsolationEnabled: false, }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/trusted_app_items_summary.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/exception_items_summary.tsx similarity index 57% rename from x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/trusted_app_items_summary.tsx rename to x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/exception_items_summary.tsx index fae65def7e2f6..661b5de242f6f 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/trusted_app_items_summary.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/exception_items_summary.tsx @@ -6,54 +6,43 @@ */ import { EuiBadge, EuiBadgeProps, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; -import React, { FC, memo, useEffect, useState } from 'react'; -import { CoreStart } from 'kibana/public'; +import React, { FC, memo } from 'react'; import { i18n } from '@kbn/i18n'; -import { useKibana } from '../../../../../../../../../../../src/plugins/kibana_react/public'; -import { TrustedAppsHttpService } from '../../../../../trusted_apps/service'; -import { GetTrustedAppsSummaryResponse } from '../../../../../../../../common/endpoint/types'; +import { GetExceptionSummaryResponse } from '../../../../../../../../common/endpoint/types'; -const SUMMARY_KEYS: Readonly> = [ +const SUMMARY_KEYS: Readonly> = [ 'windows', 'macos', 'linux', 'total', ]; -const SUMMARY_LABELS: Readonly<{ [key in keyof GetTrustedAppsSummaryResponse]: string }> = { +const SUMMARY_LABELS: Readonly<{ [key in keyof GetExceptionSummaryResponse]: string }> = { windows: i18n.translate( - 'xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppItemsSummary.windows', + 'xpack.securitySolution.endpoint.fleetCustomExtension.exceptionItemsSummary.windows', { defaultMessage: 'Windows' } ), linux: i18n.translate( - 'xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppItemsSummary.linux', + 'xpack.securitySolution.endpoint.fleetCustomExtension.exceptionItemsSummary.linux', { defaultMessage: 'Linux' } ), macos: i18n.translate( - 'xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppItemsSummary.macos', + 'xpack.securitySolution.endpoint.fleetCustomExtension.exceptionItemsSummary.macos', { defaultMessage: 'Mac' } ), total: i18n.translate( - 'xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppItemsSummary.total', + 'xpack.securitySolution.endpoint.fleetCustomExtension.exceptionItemsSummary.total', { defaultMessage: 'Total' } ), }; const CSS_BOLD: Readonly = { fontWeight: 'bold' }; -export const TrustedAppItemsSummary = memo(() => { - const { - services: { http }, - } = useKibana(); - const [stats, setStats] = useState(); - const [trustedAppsApi] = useState(() => new TrustedAppsHttpService(http)); - - useEffect(() => { - trustedAppsApi.getTrustedAppsSummary().then((response) => { - setStats(response); - }); - }, [trustedAppsApi]); +interface ExceptionItemsSummaryProps { + stats: GetExceptionSummaryResponse | undefined; +} +export const ExceptionItemsSummary = memo(({ stats }) => { return ( {SUMMARY_KEYS.map((stat) => { @@ -73,7 +62,7 @@ export const TrustedAppItemsSummary = memo(() => { ); }); -TrustedAppItemsSummary.displayName = 'TrustedAppItemsSummary'; +ExceptionItemsSummary.displayName = 'ExceptionItemsSummary'; const SummaryStat: FC<{ value: number; color?: EuiBadgeProps['color'] }> = memo( ({ children, value, color, ...commonProps }) => { diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx new file mode 100644 index 0000000000000..dc4d81c72f815 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo, useMemo, useState, useEffect } from 'react'; +import { ApplicationStart, CoreStart } from 'kibana/public'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + PackageCustomExtensionComponentProps, + pagePathGetters, +} from '../../../../../../../../../fleet/public'; +import { useKibana } from '../../../../../../../../../../../src/plugins/kibana_react/public'; +import { getEventFiltersListPath } from '../../../../../../common/routing'; +import { GetExceptionSummaryResponse } from '../../../../../../../../common/endpoint/types'; +import { PLUGIN_ID as FLEET_PLUGIN_ID } from '../../../../../../../../../fleet/common'; +import { MANAGEMENT_APP_ID } from '../../../../../../common/constants'; +import { LinkWithIcon } from './link_with_icon'; +import { ExceptionItemsSummary } from './exception_items_summary'; + +export const FleetEventFiltersCard = memo(({ pkgkey }) => { + const { + services: { + application: { getUrlForApp }, + http, + }, + } = useKibana(); + + const [stats, setStats] = useState(); + const eventFiltersListUrlPath = getEventFiltersListPath(); + + useEffect(() => { + const fetchStats = async () => { + const summary = await http.get('/api/exception_lists/_summary', { + query: { + list_id: 'endpoint_event_filters', + namespace_type: 'agnostic', + }, + }); + setStats(summary); + }; + fetchStats(); + }, [http]); + + const eventFiltersRouteState = useMemo(() => { + const fleetPackageCustomUrlPath = `#${pagePathGetters.integration_details_custom({ pkgkey })}`; + return { + backButtonLabel: i18n.translate( + 'xpack.securitySolution.endpoint.fleetCustomExtension.backButtonLabel', + { defaultMessage: 'Back to Endpoint Integration' } + ), + onBackButtonNavigateTo: [ + FLEET_PLUGIN_ID, + { + path: fleetPackageCustomUrlPath, + }, + ], + backButtonUrl: getUrlForApp(FLEET_PLUGIN_ID, { + path: fleetPackageCustomUrlPath, + }), + }; + }, [getUrlForApp, pkgkey]); + + return ( + + + + +

+ +

+
+
+ + + + + + + + + + +
+
+ ); +}); + +FleetEventFiltersCard.displayName = 'FleetEventFiltersCard'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx index fe6f82e632f73..a175f0c0b354f 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx @@ -5,8 +5,8 @@ * 2.0. */ -import React, { memo, useMemo } from 'react'; -import { ApplicationStart } from 'kibana/public'; +import React, { memo, useMemo, useState, useEffect } from 'react'; +import { ApplicationStart, CoreStart } from 'kibana/public'; import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -16,19 +16,32 @@ import { } from '../../../../../../../../../fleet/public'; import { useKibana } from '../../../../../../../../../../../src/plugins/kibana_react/public'; import { getTrustedAppsListPath } from '../../../../../../common/routing'; -import { TrustedAppsListPageRouteState } from '../../../../../../../../common/endpoint/types'; +import { + TrustedAppsListPageRouteState, + GetExceptionSummaryResponse, +} from '../../../../../../../../common/endpoint/types'; import { PLUGIN_ID as FLEET_PLUGIN_ID } from '../../../../../../../../../fleet/common'; import { MANAGEMENT_APP_ID } from '../../../../../../common/constants'; import { LinkWithIcon } from './link_with_icon'; -import { TrustedAppItemsSummary } from './trusted_app_items_summary'; +import { ExceptionItemsSummary } from './exception_items_summary'; +import { TrustedAppsHttpService } from '../../../../../trusted_apps/service'; export const FleetTrustedAppsCard = memo(({ pkgkey }) => { const { services: { application: { getUrlForApp }, + http, }, - } = useKibana<{ application: ApplicationStart }>(); + } = useKibana(); + + const [stats, setStats] = useState(); + const [trustedAppsApi] = useState(() => new TrustedAppsHttpService(http)); + useEffect(() => { + trustedAppsApi.getTrustedAppsSummary().then((response) => { + setStats(response); + }); + }, [trustedAppsApi]); const trustedAppsListUrlPath = getTrustedAppsListPath(); const trustedAppRouteState = useMemo(() => { @@ -53,7 +66,7 @@ export const FleetTrustedAppsCard = memo(( return ( - +

((

- - + + diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/index.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/index.tsx index c127a60d84ccf..094f1131d7034 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/index.tsx @@ -5,15 +5,19 @@ * 2.0. */ +import { EuiSpacer } from '@elastic/eui'; import React, { memo } from 'react'; import { PackageCustomExtensionComponentProps } from '../../../../../../../../fleet/public'; import { FleetTrustedAppsCard } from './components/fleet_trusted_apps_card'; +import { FleetEventFiltersCard } from './components/fleet_event_filters_card'; export const EndpointPackageCustomExtension = memo( (props) => { return (
+ +
); } From 3864d72a46cb3fbb3d29988703daa709ae304269 Mon Sep 17 00:00:00 2001 From: David Sanchez Soler Date: Wed, 26 May 2021 12:08:58 +0200 Subject: [PATCH 02/17] Uses aggs instead of while loop to retrieve summary data --- .../get_exception_list_summary.ts | 89 ++++++++----------- 1 file changed, 38 insertions(+), 51 deletions(-) diff --git a/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts b/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts index 0557a741e655b..6e0b473689060 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts @@ -6,7 +6,6 @@ */ import type { - FoundExceptionListItemSchema, IdOrUndefined, ListIdOrUndefined, NamespaceType, @@ -20,7 +19,6 @@ import { import { ExceptionListSoSchema } from '../../schemas/saved_objects'; import { GetExceptionListSummaryResponse } from './exception_list_client_types'; -import { findExceptionListsItem } from './find_exception_list_items'; interface GetExceptionListSummaryOptions { id: IdOrUndefined; @@ -29,6 +27,16 @@ interface GetExceptionListSummaryOptions { namespaceType: NamespaceType; } +interface ByOsAggBucketType { + key: string; + doc_count: number; +} +interface ByOsAggType { + by_os: { + buckets: ByOsAggBucketType[]; + }; +} + export const getExceptionListSummary = async ({ id, listId, @@ -36,11 +44,13 @@ export const getExceptionListSummary = async ({ namespaceType, }: GetExceptionListSummaryOptions): Promise => { const savedObjectType = getSavedObjectType({ namespaceType }); - let finalListId: string[] = listId ? [listId] : []; + let finalListId: string = listId ?? ''; + + // If id and no listId, get the list by id to use the list_id for the find below if (listId === null && id != null) { try { const savedObject = await savedObjectsClient.get(savedObjectType, id); - finalListId = [savedObject.attributes.list_id]; + finalListId = savedObject.attributes.list_id; } catch (err) { if (SavedObjectsErrorHelpers.isNotFoundError(err)) { return null; @@ -48,56 +58,33 @@ export const getExceptionListSummary = async ({ throw err; } } - } else if (listId != null) { - const savedObject = await savedObjectsClient.find({ - filter: `${savedObjectType}.attributes.list_type: list`, - perPage: 1, - search: listId, - searchFields: ['list_id'], - sortField: 'tie_breaker_id', - sortOrder: 'desc', - type: savedObjectType, - }); - if (savedObject.saved_objects[0] == null) { - return null; - } - } else { - return null; } - const summary = { - linux: 0, - macos: 0, - total: 0, - windows: 0, - }; - const perPage = 100; - let paging = true; - let page = 1; + const savedObject = await savedObjectsClient.find({ + aggs: { + by_os: { + terms: { + field: `${savedObjectType}.attributes.os_types`, + }, + }, + }, + filter: `${savedObjectType}.attributes.list_type: item`, + perPage: 0, + search: finalListId, + searchFields: ['list_id'], + sortField: 'tie_breaker_id', + sortOrder: 'desc', + type: savedObjectType, + }); - while (paging) { - const items: FoundExceptionListItemSchema | null = await findExceptionListsItem({ - filter: [], - listId: finalListId, - namespaceType: ['agnostic'], - page, - perPage, - savedObjectsClient, - sortField: undefined, - sortOrder: undefined, - }); - - if (!items) break; - - summary.total = items.total; - - for (const item of items.data) { - summary[item.os_types[0]]++; - } - - paging = (page - 1) * perPage + items.data.length < items.total; - page++; - } + const summary: GetExceptionListSummaryResponse = (savedObject.aggregations as ByOsAggType).by_os.buckets.reduce( + (acc, item: ByOsAggBucketType) => ({ + ...acc, + [item.key]: item.doc_count, + total: acc.total + item.doc_count, + }), + { linux: 0, macos: 0, total: 0, windows: 0 } + ); return summary; }; From 64e9d6552bcd9e84a7ce2beea7f6d9c97b89f9ec Mon Sep 17 00:00:00 2001 From: David Sanchez Soler Date: Wed, 26 May 2021 14:38:15 +0200 Subject: [PATCH 03/17] Add request and response types in the lists package --- .../src/request/index.ts | 1 + .../index.mock.ts | 17 +++ .../index.test.ts | 127 ++++++++++++++++++ .../summary_exception_list_schema/index.ts | 33 +++++ .../index.mock.ts | 16 +++ .../index.test.ts | 94 +++++++++++++ .../exception_list_summary_schema/index.ts | 21 +++ .../src/response/index.ts | 1 + .../routes/summary_exception_list_route.ts | 11 +- .../exception_lists/exception_list_client.ts | 4 +- .../exception_list_client_types.ts | 7 - .../get_exception_list_summary.ts | 7 +- 12 files changed, 321 insertions(+), 18 deletions(-) create mode 100644 packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.mock.ts create mode 100644 packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.mock.ts create mode 100644 packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.ts diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/index.ts index 2c71afd9e066e..3d3c41aed5a72 100644 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/index.ts +++ b/packages/kbn-securitysolution-io-ts-list-types/src/request/index.ts @@ -31,6 +31,7 @@ export * from './read_exception_list_item_schema'; export * from './read_exception_list_schema'; export * from './read_list_item_schema'; export * from './read_list_schema'; +export * from './summary_exception_list_schema'; export * from './update_endpoint_list_item_schema'; export * from './update_exception_list_item_schema'; export * from './update_exception_list_item_validation'; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.mock.ts new file mode 100644 index 0000000000000..384f093d0884a --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.mock.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ID, LIST_ID, NAMESPACE_TYPE } from '../../constants/index.mock'; + +import { SummaryExceptionListSchema } from '.'; + +export const getSummaryExceptionListSchemaMock = (): SummaryExceptionListSchema => ({ + id: ID, + list_id: LIST_ID, + namespace_type: NAMESPACE_TYPE, +}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.test.ts new file mode 100644 index 0000000000000..69e5f91085428 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.test.ts @@ -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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { getSummaryExceptionListSchemaMock } from './index.mock'; +import { SummaryExceptionListSchema, summaryExceptionListSchema } from '.'; + +describe('summary_exception_list_schema', () => { + test('it should validate a typical exception list request', () => { + const payload = getSummaryExceptionListSchemaMock(); + const decoded = summaryExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "id"', () => { + const payload = getSummaryExceptionListSchemaMock(); + delete payload.id; + const decoded = summaryExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "list_id"', () => { + const payload = getSummaryExceptionListSchemaMock(); + delete payload.list_id; + const decoded = summaryExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "namespace_type" but default to "single"', () => { + const payload = getSummaryExceptionListSchemaMock(); + delete payload.namespace_type; + const decoded = summaryExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getSummaryExceptionListSchemaMock()); + }); + + test('it should accept an undefined for "id", "list_id", "namespace_type" but default "namespace_type" to "single"', () => { + const payload = getSummaryExceptionListSchemaMock(); + delete payload.id; + delete payload.namespace_type; + delete payload.list_id; + const output = getSummaryExceptionListSchemaMock(); + delete output.id; + delete output.list_id; + const decoded = summaryExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(output); + }); + + test('it should accept an undefined for "id", "list_id"', () => { + const payload = getSummaryExceptionListSchemaMock(); + delete payload.id; + delete payload.list_id; + const decoded = summaryExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "id", "namespace_type" but default "namespace_type" to "single"', () => { + const payload = getSummaryExceptionListSchemaMock(); + delete payload.id; + delete payload.namespace_type; + const output = getSummaryExceptionListSchemaMock(); + delete output.id; + const decoded = summaryExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(output); + }); + + test('it should accept an undefined for "list_id", "namespace_type" but default "namespace_type" to "single"', () => { + const payload = getSummaryExceptionListSchemaMock(); + delete payload.namespace_type; + delete payload.list_id; + const output = getSummaryExceptionListSchemaMock(); + delete output.list_id; + const decoded = summaryExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(output); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: SummaryExceptionListSchema & { + extraKey?: string; + } = getSummaryExceptionListSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = summaryExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.ts new file mode 100644 index 0000000000000..990091882df7b --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; + +import { NamespaceType } from '../../common/default_namespace'; +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { id } from '../../common/id'; +import { list_id } from '../../common/list_id'; +import { namespace_type } from '../../common/namespace_type'; + +export const summaryExceptionListSchema = t.exact( + t.partial({ + id, + list_id, + namespace_type, // defaults to 'single' if not set during decode + }) +); + +export type SummaryExceptionListSchema = t.OutputOf; + +// This type is used after a decode since some things are defaults after a decode. +export type SummaryExceptionListSchemaDecoded = Omit< + RequiredKeepUndefined>, + 'namespace_type' +> & { + namespace_type: NamespaceType; +}; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.mock.ts new file mode 100644 index 0000000000000..6b7ce27d07194 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.mock.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ExceptionListSummarySchema } from '.'; + +export const getListSummaryResponseMock = (): ExceptionListSummarySchema => ({ + windows: 0, + linux: 1, + macos: 2, + total: 3, +}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.test.ts new file mode 100644 index 0000000000000..ea086f427451d --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.test.ts @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { getListSummaryResponseMock } from './index.mock'; +import { ExceptionListSummarySchema, exceptionListSummarySchema } from '.'; + +describe('list_summary_schema', () => { + test('it should validate a typical list summary response', () => { + const payload = getListSummaryResponseMock(); + const decoded = exceptionListSummarySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept an undefined for "windows"', () => { + const payload = getListSummaryResponseMock(); + // @ts-expect-error + delete payload.windows; + const decoded = exceptionListSummarySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "windows"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "linux"', () => { + const payload = getListSummaryResponseMock(); + // @ts-expect-error + delete payload.linux; + const decoded = exceptionListSummarySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "linux"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "macos"', () => { + const payload = getListSummaryResponseMock(); + // @ts-expect-error + delete payload.macos; + const decoded = exceptionListSummarySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "macos"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "total"', () => { + const payload = getListSummaryResponseMock(); + // @ts-expect-error + delete payload.total; + const decoded = exceptionListSummarySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "total"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: ExceptionListSummarySchema & { + extraKey?: string; + } = getListSummaryResponseMock(); + payload.extraKey = 'some new value'; + const decoded = exceptionListSummarySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.ts new file mode 100644 index 0000000000000..1aa5d53f736c3 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { _versionOrUndefined } from '../../common/underscore_version'; + +export const exceptionListSummarySchema = t.exact( + t.type({ + windows: t.number, + linux: t.number, + macos: t.number, + total: t.number, + }) +); + +export type ExceptionListSummarySchema = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/index.ts index 005e753ccf1b3..dc29bdf16ab48 100644 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/index.ts +++ b/packages/kbn-securitysolution-io-ts-list-types/src/response/index.ts @@ -16,5 +16,6 @@ export * from './found_list_item_schema'; export * from './found_list_schema'; export * from './list_item_schema'; export * from './list_schema'; +export * from './exception_list_summary_schema'; export * from './list_item_index_exist_schema'; export * from './search_list_item_schema'; diff --git a/x-pack/plugins/lists/server/routes/summary_exception_list_route.ts b/x-pack/plugins/lists/server/routes/summary_exception_list_route.ts index 21f9ca5eeffb5..4353b477fc9fe 100644 --- a/x-pack/plugins/lists/server/routes/summary_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/summary_exception_list_route.ts @@ -7,8 +7,8 @@ import { transformError } from '@kbn/securitysolution-es-utils'; import { - ReadExceptionListSchemaDecoded, - readExceptionListSchema, + SummaryExceptionListSchemaDecoded, + summaryExceptionListSchema, } from '@kbn/securitysolution-io-ts-list-types'; import { EXCEPTION_LIST_URL } from '@kbn/securitysolution-list-constants'; @@ -29,9 +29,10 @@ export const summaryExceptionListRoute = (router: ListsPluginRouter): void => { }, path: `${EXCEPTION_LIST_URL}/_summary`, validate: { - query: buildRouteValidation( - readExceptionListSchema - ), + query: buildRouteValidation< + typeof summaryExceptionListSchema, + SummaryExceptionListSchemaDecoded + >(summaryExceptionListSchema), }, }, async (context, request, response) => { diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts index 87ae890f4c424..4ccff2dd000b9 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts @@ -9,6 +9,7 @@ import { SavedObjectsClientContract } from 'kibana/server'; import type { ExceptionListItemSchema, ExceptionListSchema, + ExceptionListSummarySchema, FoundExceptionListItemSchema, FoundExceptionListSchema, } from '@kbn/securitysolution-io-ts-list-types'; @@ -32,7 +33,6 @@ import { GetExceptionListItemOptions, GetExceptionListOptions, GetExceptionListSummaryOptions, - GetExceptionListSummaryResponse, UpdateEndpointListItemOptions, UpdateExceptionListItemOptions, UpdateExceptionListOptions, @@ -79,7 +79,7 @@ export class ExceptionListClient { listId, id, namespaceType, - }: GetExceptionListSummaryOptions): Promise => { + }: GetExceptionListSummaryOptions): Promise => { const { savedObjectsClient } = this; return getExceptionListSummary({ id, listId, namespaceType, savedObjectsClient }); }; diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts index acaae9a8da211..b734d3a7b1a3b 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts @@ -62,13 +62,6 @@ export interface GetExceptionListSummaryOptions { namespaceType: NamespaceType; } -export interface GetExceptionListSummaryResponse { - windows: number; - linux: number; - macos: number; - total: number; -} - export interface CreateExceptionListOptions { listId: ListId; namespaceType: NamespaceType; diff --git a/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts b/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts index 6e0b473689060..79e3d0c840897 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts @@ -8,6 +8,7 @@ import type { IdOrUndefined, ListIdOrUndefined, + ListSummarySchema, NamespaceType, } from '@kbn/securitysolution-io-ts-list-types'; import { getSavedObjectType } from '@kbn/securitysolution-list-utils'; @@ -18,8 +19,6 @@ import { } from '../../../../../../src/core/server/'; import { ExceptionListSoSchema } from '../../schemas/saved_objects'; -import { GetExceptionListSummaryResponse } from './exception_list_client_types'; - interface GetExceptionListSummaryOptions { id: IdOrUndefined; listId: ListIdOrUndefined; @@ -42,7 +41,7 @@ export const getExceptionListSummary = async ({ listId, savedObjectsClient, namespaceType, -}: GetExceptionListSummaryOptions): Promise => { +}: GetExceptionListSummaryOptions): Promise => { const savedObjectType = getSavedObjectType({ namespaceType }); let finalListId: string = listId ?? ''; @@ -77,7 +76,7 @@ export const getExceptionListSummary = async ({ type: savedObjectType, }); - const summary: GetExceptionListSummaryResponse = (savedObject.aggregations as ByOsAggType).by_os.buckets.reduce( + const summary: ListSummarySchema = (savedObject.aggregations as ByOsAggType).by_os.buckets.reduce( (acc, item: ByOsAggBucketType) => ({ ...acc, [item.key]: item.doc_count, From 06a8f0cb89a0f1ad5eec8245f8016edac4690180 Mon Sep 17 00:00:00 2001 From: David Sanchez Soler Date: Wed, 26 May 2021 14:43:19 +0200 Subject: [PATCH 04/17] Fixes old import --- .../services/exception_lists/get_exception_list_summary.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts b/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts index 79e3d0c840897..21dbdae75b426 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts @@ -6,9 +6,9 @@ */ import type { + ExceptionListSummarySchema, IdOrUndefined, ListIdOrUndefined, - ListSummarySchema, NamespaceType, } from '@kbn/securitysolution-io-ts-list-types'; import { getSavedObjectType } from '@kbn/securitysolution-list-utils'; @@ -41,7 +41,7 @@ export const getExceptionListSummary = async ({ listId, savedObjectsClient, namespaceType, -}: GetExceptionListSummaryOptions): Promise => { +}: GetExceptionListSummaryOptions): Promise => { const savedObjectType = getSavedObjectType({ namespaceType }); let finalListId: string = listId ?? ''; @@ -76,7 +76,7 @@ export const getExceptionListSummary = async ({ type: savedObjectType, }); - const summary: ListSummarySchema = (savedObject.aggregations as ByOsAggType).by_os.buckets.reduce( + const summary: ExceptionListSummarySchema = (savedObject.aggregations as ByOsAggType).by_os.buckets.reduce( (acc, item: ByOsAggBucketType) => ({ ...acc, [item.key]: item.doc_count, From 0ad0902949a4a6c7ab8e886c1704c5de9df94017 Mon Sep 17 00:00:00 2001 From: David Sanchez Soler Date: Wed, 26 May 2021 15:47:57 +0200 Subject: [PATCH 05/17] Removes old i18n keys --- x-pack/plugins/translations/translations/zh-CN.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 8f0694e02ad91..8c46745559f9e 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -20947,10 +20947,6 @@ "xpack.securitySolution.endpoint.details.policyStatusValue": "{policyStatus, select, success {成功} warning {警告} failure {失败} other {未知}}", "xpack.securitySolution.endpoint.fleetCustomExtension.backButtonLabel": "返回至终端集成", "xpack.securitySolution.endpoint.fleetCustomExtension.manageTrustedAppLinkLabel": "管理受信任的应用程序", - "xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppItemsSummary.linux": "Linux", - "xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppItemsSummary.macos": "Mac", - "xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppItemsSummary.total": "合计", - "xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppItemsSummary.windows": "Windows", "xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppsLabel": "受信任的应用程序", "xpack.securitySolution.endpoint.ingestManager.createPackagePolicy.endpointConfiguration": "我们将使用建议的默认值保存您的集成。稍后,您可以通过在代理策略中编辑 Endpoint Security 集成对其进行更改。", "xpack.securitySolution.endpoint.ingestToastMessage": "Fleet 在设置期间失败。", From 662c59b6cf56dc22f9f6de29188f52bac3b29963 Mon Sep 17 00:00:00 2001 From: David Sanchez Soler Date: Wed, 26 May 2021 16:02:59 +0200 Subject: [PATCH 06/17] Removes more old i18n keys --- x-pack/plugins/translations/translations/ja-JP.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 417c8360d0df3..96d8bfa642ad3 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -20643,10 +20643,6 @@ "xpack.securitySolution.endpoint.details.policyStatusValue": "{policyStatus, select, success {成功} warning {警告} failure {失敗} other {不明}}", "xpack.securitySolution.endpoint.fleetCustomExtension.backButtonLabel": "エンドポイント統合に戻る", "xpack.securitySolution.endpoint.fleetCustomExtension.manageTrustedAppLinkLabel": "信頼できるアプリケーションを管理", - "xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppItemsSummary.linux": "Linux", - "xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppItemsSummary.macos": "Mac", - "xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppItemsSummary.total": "合計", - "xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppItemsSummary.windows": "Windows", "xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppsLabel": "信頼できるアプリケーション", "xpack.securitySolution.endpoint.ingestManager.createPackagePolicy.endpointConfiguration": "推奨のデフォルト値で統合が保存されます。後からこれを変更するには、エージェントポリシー内で Endpoint Security 統合を編集します。", "xpack.securitySolution.endpoint.ingestToastMessage": "Fleetが設定中に失敗しました。", From aca55cdac298f1ee10cbff1a357089af33fe9d0b Mon Sep 17 00:00:00 2001 From: David Sanchez Soler Date: Wed, 26 May 2021 17:11:55 +0200 Subject: [PATCH 07/17] Use consts for exception lists url and endpoint event filter list id --- .../components/fleet_event_filters_card.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx index dc4d81c72f815..a476f656baf01 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx @@ -10,6 +10,10 @@ import { ApplicationStart, CoreStart } from 'kibana/public'; import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import { + ENDPOINT_EVENT_FILTERS_LIST_ID, + EXCEPTION_LIST_URL, +} from '@kbn/securitysolution-list-constants'; import { PackageCustomExtensionComponentProps, pagePathGetters, @@ -35,9 +39,9 @@ export const FleetEventFiltersCard = memo( useEffect(() => { const fetchStats = async () => { - const summary = await http.get('/api/exception_lists/_summary', { + const summary = await http.get(`${EXCEPTION_LIST_URL}/_summary`, { query: { - list_id: 'endpoint_event_filters', + list_id: ENDPOINT_EVENT_FILTERS_LIST_ID, namespace_type: 'agnostic', }, }); From 84d46a417594a90e9a543be4ba1d12c98f864f5e Mon Sep 17 00:00:00 2001 From: David Sanchez Soler Date: Thu, 27 May 2021 11:16:18 +0200 Subject: [PATCH 08/17] Uses event filters service to retrieve summary data --- .../pages/event_filters/service/index.ts | 13 +++++++++++++ .../pages/event_filters/store/middleware.test.ts | 1 + .../management/pages/event_filters/types.ts | 2 ++ .../components/fleet_event_filters_card.tsx | 15 ++++----------- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/service/index.ts b/x-pack/plugins/security_solution/public/management/pages/event_filters/service/index.ts index 8863acaafbf5a..05729c6d1c14d 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/service/index.ts +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/service/index.ts @@ -11,6 +11,7 @@ import type { ExceptionListItemSchema, CreateExceptionListItemSchema, UpdateExceptionListItemSchema, + ExceptionListSummarySchema, } from '@kbn/securitysolution-io-ts-list-types'; import { ENDPOINT_EVENT_FILTERS_LIST_ID } from '@kbn/securitysolution-list-constants'; @@ -102,4 +103,16 @@ export class EventFiltersHttpService implements EventFiltersService { }, }); } + + async getSummary(): Promise { + return (await this.httpWrapper()).get( + `${EXCEPTION_LIST_URL}/summary`, + { + query: { + list_id: ENDPOINT_EVENT_FILTERS_LIST_ID, + namespace_type: 'agnostic', + }, + } + ); + } } diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/store/middleware.test.ts b/x-pack/plugins/security_solution/public/management/pages/event_filters/store/middleware.test.ts index b55a32a937c45..5229e4078eb0d 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/store/middleware.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/store/middleware.test.ts @@ -29,6 +29,7 @@ const createEventFiltersServiceMock = (): jest.Mocked => ({ getOne: jest.fn(), updateOne: jest.fn(), deleteOne: jest.fn(), + getSummary: jest.fn(), }); const createStoreSetup = (eventFiltersService: EventFiltersService) => { diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/types.ts b/x-pack/plugins/security_solution/public/management/pages/event_filters/types.ts index be6689b7e5b57..3bcf6a3369302 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/types.ts +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/types.ts @@ -10,6 +10,7 @@ import type { CreateExceptionListItemSchema, ExceptionListItemSchema, UpdateExceptionListItemSchema, + ExceptionListSummarySchema, } from '@kbn/securitysolution-io-ts-list-types'; import { AsyncResourceState } from '../../state/async_resource_state'; import { Immutable } from '../../../../common/endpoint/types'; @@ -49,6 +50,7 @@ export interface EventFiltersService { getOne(id: string): Promise; updateOne(exception: Immutable): Promise; deleteOne(id: string): Promise; + getSummary(): Promise; } export interface EventFiltersListPageData { diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx index a476f656baf01..9278b6dd58e76 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx @@ -10,10 +10,6 @@ import { ApplicationStart, CoreStart } from 'kibana/public'; import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { - ENDPOINT_EVENT_FILTERS_LIST_ID, - EXCEPTION_LIST_URL, -} from '@kbn/securitysolution-list-constants'; import { PackageCustomExtensionComponentProps, pagePathGetters, @@ -25,6 +21,7 @@ import { PLUGIN_ID as FLEET_PLUGIN_ID } from '../../../../../../../../../fleet/c import { MANAGEMENT_APP_ID } from '../../../../../../common/constants'; import { LinkWithIcon } from './link_with_icon'; import { ExceptionItemsSummary } from './exception_items_summary'; +import { EventFiltersHttpService } from '../../../../../event_filters/service'; export const FleetEventFiltersCard = memo(({ pkgkey }) => { const { @@ -36,19 +33,15 @@ export const FleetEventFiltersCard = memo( const [stats, setStats] = useState(); const eventFiltersListUrlPath = getEventFiltersListPath(); + const eventFiltersApi = useMemo(() => new EventFiltersHttpService(http), [http]); useEffect(() => { const fetchStats = async () => { - const summary = await http.get(`${EXCEPTION_LIST_URL}/_summary`, { - query: { - list_id: ENDPOINT_EVENT_FILTERS_LIST_ID, - namespace_type: 'agnostic', - }, - }); + const summary = await eventFiltersApi.getSummary(); setStats(summary); }; fetchStats(); - }, [http]); + }, [eventFiltersApi]); const eventFiltersRouteState = useMemo(() => { const fleetPackageCustomUrlPath = `#${pagePathGetters.integration_details_custom({ pkgkey })}`; From e42bf6134ebc69fbac98c43bc4eb75fab2869c91 Mon Sep 17 00:00:00 2001 From: David Sanchez Soler Date: Thu, 27 May 2021 11:17:37 +0200 Subject: [PATCH 09/17] Fixes addressed pr comments such as changing the route without underscore, adding aggs type, validating response, and more --- .../index.test.ts | 19 +++++++++---------- .../exception_list_summary_schema/index.ts | 10 +++++----- .../routes/summary_exception_list_route.ts | 11 +++++++++-- .../get_exception_list_summary.ts | 8 ++++++-- 4 files changed, 29 insertions(+), 19 deletions(-) diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.test.ts index 69e5f91085428..ade015b0d26bf 100644 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.test.ts +++ b/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.test.ts @@ -7,7 +7,6 @@ */ import { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; import { getSummaryExceptionListSchemaMock } from './index.mock'; @@ -18,7 +17,7 @@ describe('summary_exception_list_schema', () => { const payload = getSummaryExceptionListSchemaMock(); const decoded = summaryExceptionListSchema.decode(payload); const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); + const message = foldLeftRight(checked); expect(getPaths(left(message.errors))).toEqual([]); expect(message.schema).toEqual(payload); @@ -29,7 +28,7 @@ describe('summary_exception_list_schema', () => { delete payload.id; const decoded = summaryExceptionListSchema.decode(payload); const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); + const message = foldLeftRight(checked); expect(getPaths(left(message.errors))).toEqual([]); expect(message.schema).toEqual(payload); @@ -40,7 +39,7 @@ describe('summary_exception_list_schema', () => { delete payload.list_id; const decoded = summaryExceptionListSchema.decode(payload); const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); + const message = foldLeftRight(checked); expect(getPaths(left(message.errors))).toEqual([]); expect(message.schema).toEqual(payload); @@ -51,7 +50,7 @@ describe('summary_exception_list_schema', () => { delete payload.namespace_type; const decoded = summaryExceptionListSchema.decode(payload); const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); + const message = foldLeftRight(checked); expect(getPaths(left(message.errors))).toEqual([]); expect(message.schema).toEqual(getSummaryExceptionListSchemaMock()); @@ -67,7 +66,7 @@ describe('summary_exception_list_schema', () => { delete output.list_id; const decoded = summaryExceptionListSchema.decode(payload); const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); + const message = foldLeftRight(checked); expect(getPaths(left(message.errors))).toEqual([]); expect(message.schema).toEqual(output); @@ -79,7 +78,7 @@ describe('summary_exception_list_schema', () => { delete payload.list_id; const decoded = summaryExceptionListSchema.decode(payload); const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); + const message = foldLeftRight(checked); expect(getPaths(left(message.errors))).toEqual([]); expect(message.schema).toEqual(payload); @@ -93,7 +92,7 @@ describe('summary_exception_list_schema', () => { delete output.id; const decoded = summaryExceptionListSchema.decode(payload); const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); + const message = foldLeftRight(checked); expect(getPaths(left(message.errors))).toEqual([]); expect(message.schema).toEqual(output); @@ -107,7 +106,7 @@ describe('summary_exception_list_schema', () => { delete output.list_id; const decoded = summaryExceptionListSchema.decode(payload); const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); + const message = foldLeftRight(checked); expect(getPaths(left(message.errors))).toEqual([]); expect(message.schema).toEqual(output); @@ -120,7 +119,7 @@ describe('summary_exception_list_schema', () => { payload.extraKey = 'some new value'; const decoded = summaryExceptionListSchema.decode(payload); const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); + const message = foldLeftRight(checked); expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); expect(message.schema).toEqual({}); }); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.ts index 1aa5d53f736c3..4c0cc8301dbf7 100644 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.ts +++ b/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.ts @@ -6,15 +6,15 @@ * Side Public License, v 1. */ +import { PositiveInteger } from '@kbn/securitysolution-io-ts-types'; import * as t from 'io-ts'; -import { _versionOrUndefined } from '../../common/underscore_version'; export const exceptionListSummarySchema = t.exact( t.type({ - windows: t.number, - linux: t.number, - macos: t.number, - total: t.number, + windows: PositiveInteger, + linux: PositiveInteger, + macos: PositiveInteger, + total: PositiveInteger, }) ); diff --git a/x-pack/plugins/lists/server/routes/summary_exception_list_route.ts b/x-pack/plugins/lists/server/routes/summary_exception_list_route.ts index 4353b477fc9fe..0db189fb70759 100644 --- a/x-pack/plugins/lists/server/routes/summary_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/summary_exception_list_route.ts @@ -5,9 +5,11 @@ * 2.0. */ +import { validate } from '@kbn/securitysolution-io-ts-utils'; import { transformError } from '@kbn/securitysolution-es-utils'; import { SummaryExceptionListSchemaDecoded, + exceptionListSummarySchema, summaryExceptionListSchema, } from '@kbn/securitysolution-io-ts-list-types'; import { EXCEPTION_LIST_URL } from '@kbn/securitysolution-list-constants'; @@ -27,7 +29,7 @@ export const summaryExceptionListRoute = (router: ListsPluginRouter): void => { options: { tags: ['access:lists-summary'], }, - path: `${EXCEPTION_LIST_URL}/_summary`, + path: `${EXCEPTION_LIST_URL}/summary`, validate: { query: buildRouteValidation< typeof summaryExceptionListSchema, @@ -52,7 +54,12 @@ export const summaryExceptionListRoute = (router: ListsPluginRouter): void => { statusCode: 404, }); } else { - return response.ok({ body: exceptionListSummary ?? {} }); + const [validated, errors] = validate(exceptionListSummary, exceptionListSummarySchema); + if (errors != null) { + return response.ok({ body: exceptionListSummary }); + } else { + return response.ok({ body: validated ?? {} }); + } } } else { return siemResponse.error({ body: 'id or list_id required', statusCode: 400 }); diff --git a/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts b/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts index 21dbdae75b426..f5722ea26ccf7 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts @@ -59,7 +59,7 @@ export const getExceptionListSummary = async ({ } } - const savedObject = await savedObjectsClient.find({ + const savedObject = await savedObjectsClient.find({ aggs: { by_os: { terms: { @@ -76,7 +76,11 @@ export const getExceptionListSummary = async ({ type: savedObjectType, }); - const summary: ExceptionListSummarySchema = (savedObject.aggregations as ByOsAggType).by_os.buckets.reduce( + if (!savedObject.aggregations) { + return null; + } + + const summary: ExceptionListSummarySchema = savedObject.aggregations.by_os.buckets.reduce( (acc, item: ByOsAggBucketType) => ({ ...acc, [item.key]: item.doc_count, From 9f6b620147c0200d3b5efe62cff0c97a788c253b Mon Sep 17 00:00:00 2001 From: David Sanchez Soler Date: Thu, 27 May 2021 11:18:13 +0200 Subject: [PATCH 10/17] Uses useMemo instead of useState to memoize object --- .../components/fleet_trusted_apps_card.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx index a175f0c0b354f..643daa065d486 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx @@ -35,7 +35,7 @@ export const FleetTrustedAppsCard = memo(( } = useKibana(); const [stats, setStats] = useState(); - const [trustedAppsApi] = useState(() => new TrustedAppsHttpService(http)); + const trustedAppsApi = useMemo(() => new TrustedAppsHttpService(http), [http]); useEffect(() => { trustedAppsApi.getTrustedAppsSummary().then((response) => { From 5b17fd5c6c6382f4e181218d422a157649be1aa2 Mon Sep 17 00:00:00 2001 From: David Sanchez Soler Date: Thu, 27 May 2021 13:38:08 +0200 Subject: [PATCH 11/17] Add new e2e test for summart endpoint --- .../security_and_spaces/tests/index.ts | 1 + .../tests/summary_exception_lists.ts | 93 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 x-pack/test/lists_api_integration/security_and_spaces/tests/summary_exception_lists.ts diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/index.ts index 61ca693936643..89a1183da6790 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/index.ts +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/index.ts @@ -35,5 +35,6 @@ export default ({ loadTestFile }: FtrProviderContext): void => { loadTestFile(require.resolve('./find_exception_lists')); loadTestFile(require.resolve('./find_exception_list_items')); loadTestFile(require.resolve('./read_list_privileges')); + loadTestFile(require.resolve('./summary_exception_lists')); }); }; diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/summary_exception_lists.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/summary_exception_lists.ts new file mode 100644 index 0000000000000..15ed970db6007 --- /dev/null +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/summary_exception_lists.ts @@ -0,0 +1,93 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +import { EXCEPTION_LIST_URL, EXCEPTION_LIST_ITEM_URL } from '@kbn/securitysolution-list-constants'; +import { LIST_ID } from '../../../../plugins/lists/common/constants.mock'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { getCreateExceptionListMinimalSchemaMock } from '../../../../plugins/lists/common/schemas/request/create_exception_list_schema.mock'; +import { getCreateExceptionListItemMinimalSchemaMock } from '../../../../plugins/lists/common/schemas/request/create_exception_list_item_schema.mock'; +import { createListsIndex, deleteListsIndex, deleteAllExceptions } from '../../utils'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const es = getService('es'); + + describe('summary_exception_lists', () => { + describe('summary exception lists', () => { + afterEach(async () => { + await deleteListsIndex(supertest); + await deleteAllExceptions(es); + }); + beforeEach(async () => { + await createListsIndex(supertest); + }); + + it('should give a validation error if the list_id and the id are not supplied', async () => { + const { body } = await supertest + .get(`${EXCEPTION_LIST_URL}/summary`) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListMinimalSchemaMock()) + .expect(400); + + expect(body).to.eql({ + message: 'id or list_id required', + status_code: 400, + }); + }); + + it('should return init summary when there are no items created', async () => { + const { body } = await supertest + .get(`${EXCEPTION_LIST_URL}/summary?list_id=${LIST_ID}`) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + expect(body).to.eql({ + linux: 0, + macos: 0, + total: 0, + windows: 0, + }); + }); + + it('should return right summary when there are items created', async () => { + await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListMinimalSchemaMock()) + .expect(200); + + const item = getCreateExceptionListItemMinimalSchemaMock(); + + for (const os of ['windows', 'linux', 'macos']) { + await supertest + .post(EXCEPTION_LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send({ ...item, os_types: [os], item_id: `${item.item_id}-${os}` }) + .expect(200); + } + + const { body } = await supertest + .get(`${EXCEPTION_LIST_URL}/summary?list_id=${LIST_ID}`) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + delete body.cursor; + expect(body).to.eql({ + linux: 1, + macos: 1, + total: 3, + windows: 1, + }); + }); + }); + }); +}; From 39c8133020fcec4dbb55a2d8122973c5c39ec01f Mon Sep 17 00:00:00 2001 From: David Sanchez Soler Date: Thu, 27 May 2021 14:44:30 +0200 Subject: [PATCH 12/17] Handle api errors on event filters and trusted apps summary api calls --- .../components/fleet_event_filters_card.tsx | 20 ++++++++++++---- .../components/fleet_trusted_apps_card.tsx | 24 +++++++++++++++---- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx index 9278b6dd58e76..9efb639694b10 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx @@ -19,6 +19,7 @@ import { getEventFiltersListPath } from '../../../../../../common/routing'; import { GetExceptionSummaryResponse } from '../../../../../../../../common/endpoint/types'; import { PLUGIN_ID as FLEET_PLUGIN_ID } from '../../../../../../../../../fleet/common'; import { MANAGEMENT_APP_ID } from '../../../../../../common/constants'; +import { useToasts } from '../../../../../../../common/lib/kibana'; import { LinkWithIcon } from './link_with_icon'; import { ExceptionItemsSummary } from './exception_items_summary'; import { EventFiltersHttpService } from '../../../../../event_filters/service'; @@ -30,18 +31,29 @@ export const FleetEventFiltersCard = memo( http, }, } = useKibana(); - + const toasts = useToasts(); const [stats, setStats] = useState(); const eventFiltersListUrlPath = getEventFiltersListPath(); const eventFiltersApi = useMemo(() => new EventFiltersHttpService(http), [http]); useEffect(() => { const fetchStats = async () => { - const summary = await eventFiltersApi.getSummary(); - setStats(summary); + try { + const summary = await eventFiltersApi.getSummary(); + setStats(summary); + } catch (err) { + toasts.addDanger( + i18n.translate( + 'xpack.securitySolution.endpoint.fleetCustomExtension.eventFiltersSummaryError', + { + defaultMessage: 'There was an error trying to fetch event filters stats', + } + ) + ); + } }; fetchStats(); - }, [eventFiltersApi]); + }, [eventFiltersApi, toasts]); const eventFiltersRouteState = useMemo(() => { const fleetPackageCustomUrlPath = `#${pagePathGetters.integration_details_custom({ pkgkey })}`; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx index 643daa065d486..823db1b6c16cd 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx @@ -22,6 +22,7 @@ import { } from '../../../../../../../../common/endpoint/types'; import { PLUGIN_ID as FLEET_PLUGIN_ID } from '../../../../../../../../../fleet/common'; import { MANAGEMENT_APP_ID } from '../../../../../../common/constants'; +import { useToasts } from '../../../../../../../common/lib/kibana'; import { LinkWithIcon } from './link_with_icon'; import { ExceptionItemsSummary } from './exception_items_summary'; import { TrustedAppsHttpService } from '../../../../../trusted_apps/service'; @@ -33,15 +34,28 @@ export const FleetTrustedAppsCard = memo(( http, }, } = useKibana(); - + const toasts = useToasts(); const [stats, setStats] = useState(); const trustedAppsApi = useMemo(() => new TrustedAppsHttpService(http), [http]); useEffect(() => { - trustedAppsApi.getTrustedAppsSummary().then((response) => { - setStats(response); - }); - }, [trustedAppsApi]); + const fetchStats = async () => { + try { + const response = await trustedAppsApi.getTrustedAppsSummary(); + setStats(response); + } catch (err) { + toasts.addDanger( + i18n.translate( + 'xpack.securitySolution.endpoint.fleetCustomExtension.eventFiltersSummaryError', + { + defaultMessage: 'There was an error trying to fetch trusted apps stats', + } + ) + ); + } + }; + fetchStats(); + }, [toasts, trustedAppsApi]); const trustedAppsListUrlPath = getTrustedAppsListPath(); const trustedAppRouteState = useMemo(() => { From b0d77b2ac4a9976118301b8c1dbb85e36d319791 Mon Sep 17 00:00:00 2001 From: David Sanchez Soler Date: Thu, 27 May 2021 14:48:49 +0200 Subject: [PATCH 13/17] Add api error message to the toast --- .../components/fleet_event_filters_card.tsx | 5 +++-- .../components/fleet_trusted_apps_card.tsx | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx index 9efb639694b10..5c1e37eb3e44d 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx @@ -41,12 +41,13 @@ export const FleetEventFiltersCard = memo( try { const summary = await eventFiltersApi.getSummary(); setStats(summary); - } catch (err) { + } catch (error) { toasts.addDanger( i18n.translate( 'xpack.securitySolution.endpoint.fleetCustomExtension.eventFiltersSummaryError', { - defaultMessage: 'There was an error trying to fetch event filters stats', + defaultMessage: 'There was an error trying to fetch event filters stats: "{error}"', + values: { error }, } ) ); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx index 823db1b6c16cd..882dca2f449f8 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx @@ -43,12 +43,13 @@ export const FleetTrustedAppsCard = memo(( try { const response = await trustedAppsApi.getTrustedAppsSummary(); setStats(response); - } catch (err) { + } catch (error) { toasts.addDanger( i18n.translate( 'xpack.securitySolution.endpoint.fleetCustomExtension.eventFiltersSummaryError', { - defaultMessage: 'There was an error trying to fetch trusted apps stats', + defaultMessage: 'There was an error trying to fetch trusted apps stats: "{error}"', + values: { error }, } ) ); From 9324dc39b8c71d510afe6365ec9cccb923744b17 Mon Sep 17 00:00:00 2001 From: David Sanchez Soler Date: Thu, 27 May 2021 15:00:39 +0200 Subject: [PATCH 14/17] Fix wrong i18n key --- .../components/fleet_trusted_apps_card.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx index 882dca2f449f8..be92e4f325beb 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx @@ -46,7 +46,7 @@ export const FleetTrustedAppsCard = memo(( } catch (error) { toasts.addDanger( i18n.translate( - 'xpack.securitySolution.endpoint.fleetCustomExtension.eventFiltersSummaryError', + 'xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppsSummaryError', { defaultMessage: 'There was an error trying to fetch trusted apps stats: "{error}"', values: { error }, From 0cc6766dd8c46a3702d001f9077851eca49549fe Mon Sep 17 00:00:00 2001 From: David Sanchez Soler Date: Thu, 27 May 2021 17:32:38 +0200 Subject: [PATCH 15/17] Change span tag by react fragment --- .../components/fleet_event_filters_card.tsx | 4 ++-- .../components/fleet_trusted_apps_card.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx index 5c1e37eb3e44d..b3794c261213f 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx @@ -92,7 +92,7 @@ export const FleetEventFiltersCard = memo(
- + <> ( defaultMessage="Manage event filters" /> - +
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx index be92e4f325beb..b7cabe814dbc2 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx @@ -95,7 +95,7 @@ export const FleetTrustedAppsCard = memo(( - + <> (( defaultMessage="Manage trusted applications" /> - +
From 29014e8fc27a277381e1d8697e563e9755775bb2 Mon Sep 17 00:00:00 2001 From: David Sanchez Soler Date: Fri, 28 May 2021 10:37:06 +0200 Subject: [PATCH 16/17] Uses styled components instead of modify compontent style directly and small improvements on test -> ts --- .../components/exception_items_summary.tsx | 9 ++----- .../components/fleet_event_filters_card.tsx | 19 +++++++------- .../components/fleet_trusted_apps_card.tsx | 19 +++++++------- .../components/styled_components.tsx | 24 ++++++++++++++++++ .../tests/summary_exception_lists.ts | 25 +++++++++++-------- 5 files changed, 61 insertions(+), 35 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/styled_components.tsx diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/exception_items_summary.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/exception_items_summary.tsx index 661b5de242f6f..f42304ffb89ae 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/exception_items_summary.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/exception_items_summary.tsx @@ -44,7 +44,7 @@ interface ExceptionItemsSummaryProps { export const ExceptionItemsSummary = memo(({ stats }) => { return ( - + {SUMMARY_KEYS.map((stat) => { return ( @@ -68,12 +68,7 @@ const SummaryStat: FC<{ value: number; color?: EuiBadgeProps['color'] }> = memo( ({ children, value, color, ...commonProps }) => { return ( - + {children} diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx index b3794c261213f..6f368a89eb5f9 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx @@ -7,7 +7,7 @@ import React, { memo, useMemo, useState, useEffect } from 'react'; import { ApplicationStart, CoreStart } from 'kibana/public'; -import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; +import { EuiPanel, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { @@ -23,6 +23,7 @@ import { useToasts } from '../../../../../../../common/lib/kibana'; import { LinkWithIcon } from './link_with_icon'; import { ExceptionItemsSummary } from './exception_items_summary'; import { EventFiltersHttpService } from '../../../../../event_filters/service'; +import { StyledEuiFlexGridGroup, StyledEuiFlexGridItem } from './styled_components'; export const FleetEventFiltersCard = memo(({ pkgkey }) => { const { @@ -77,8 +78,8 @@ export const FleetEventFiltersCard = memo( return ( - - + +

( />

-
- + + - - + + <> ( /> - -
+ +
); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx index b7cabe814dbc2..ec1479643999a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx @@ -7,7 +7,7 @@ import React, { memo, useMemo, useState, useEffect } from 'react'; import { ApplicationStart, CoreStart } from 'kibana/public'; -import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; +import { EuiPanel, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { @@ -26,6 +26,7 @@ import { useToasts } from '../../../../../../../common/lib/kibana'; import { LinkWithIcon } from './link_with_icon'; import { ExceptionItemsSummary } from './exception_items_summary'; import { TrustedAppsHttpService } from '../../../../../trusted_apps/service'; +import { StyledEuiFlexGridGroup, StyledEuiFlexGridItem } from './styled_components'; export const FleetTrustedAppsCard = memo(({ pkgkey }) => { const { @@ -80,8 +81,8 @@ export const FleetTrustedAppsCard = memo(( return ( - - + +

(( />

-
- + + - - + + <> (( /> - -
+ +
); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/styled_components.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/styled_components.tsx new file mode 100644 index 0000000000000..8791f7fa87283 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/styled_components.tsx @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import styled from 'styled-components'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +export const StyledEuiFlexGridGroup = styled(EuiFlexGroup)` + display: grid; + grid-template-columns: 25% 45% 30%; + grid-template-areas: 'title summary link'; +`; + +export const StyledEuiFlexGridItem = styled(EuiFlexItem)<{ + gridArea: string; + alignItems?: string; +}>` + grid-area: ${({ gridArea }) => gridArea}; + align-items: ${({ alignItems }) => alignItems ?? 'center'}; + margin: 0px; + padding: 12px; +`; diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/summary_exception_lists.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/summary_exception_lists.ts index 15ed970db6007..13ba7da4c7aaa 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/summary_exception_lists.ts +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/summary_exception_lists.ts @@ -7,6 +7,7 @@ import expect from '@kbn/expect'; +import { ExceptionListSummarySchema } from '@kbn/securitysolution-io-ts-list-types'; import { EXCEPTION_LIST_URL, EXCEPTION_LIST_ITEM_URL } from '@kbn/securitysolution-list-constants'; import { LIST_ID } from '../../../../plugins/lists/common/constants.mock'; import { FtrProviderContext } from '../../common/ftr_provider_context'; @@ -14,6 +15,9 @@ import { getCreateExceptionListMinimalSchemaMock } from '../../../../plugins/lis import { getCreateExceptionListItemMinimalSchemaMock } from '../../../../plugins/lists/common/schemas/request/create_exception_list_item_schema.mock'; import { createListsIndex, deleteListsIndex, deleteAllExceptions } from '../../utils'; +interface SummaryResponseType { + body: ExceptionListSummarySchema; +} // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); @@ -21,13 +25,13 @@ export default ({ getService }: FtrProviderContext) => { describe('summary_exception_lists', () => { describe('summary exception lists', () => { + beforeEach(async () => { + await createListsIndex(supertest); + }); afterEach(async () => { await deleteListsIndex(supertest); await deleteAllExceptions(es); }); - beforeEach(async () => { - await createListsIndex(supertest); - }); it('should give a validation error if the list_id and the id are not supplied', async () => { const { body } = await supertest @@ -43,18 +47,19 @@ export default ({ getService }: FtrProviderContext) => { }); it('should return init summary when there are no items created', async () => { - const { body } = await supertest + const { body }: SummaryResponseType = await supertest .get(`${EXCEPTION_LIST_URL}/summary?list_id=${LIST_ID}`) .set('kbn-xsrf', 'true') .send() .expect(200); - expect(body).to.eql({ + const expected: ExceptionListSummarySchema = { linux: 0, macos: 0, total: 0, windows: 0, - }); + }; + expect(body).to.eql(expected); }); it('should return right summary when there are items created', async () => { @@ -74,19 +79,19 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); } - const { body } = await supertest + const { body }: SummaryResponseType = await supertest .get(`${EXCEPTION_LIST_URL}/summary?list_id=${LIST_ID}`) .set('kbn-xsrf', 'true') .send() .expect(200); - delete body.cursor; - expect(body).to.eql({ + const expected: ExceptionListSummarySchema = { linux: 1, macos: 1, total: 3, windows: 1, - }); + }; + expect(body).to.eql(expected); }); }); }); From 8993d4ea987bc298ebb3d44b8162a9144188efd3 Mon Sep 17 00:00:00 2001 From: David Sanchez Soler Date: Fri, 28 May 2021 10:55:24 +0200 Subject: [PATCH 17/17] Adds curls script for summary route --- .../server/scripts/summary_exception_list.sh | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100755 x-pack/plugins/lists/server/scripts/summary_exception_list.sh diff --git a/x-pack/plugins/lists/server/scripts/summary_exception_list.sh b/x-pack/plugins/lists/server/scripts/summary_exception_list.sh new file mode 100755 index 0000000000000..54daeee7cb387 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/summary_exception_list.sh @@ -0,0 +1,25 @@ +#!/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 +# 2.0; you may not use this file except in compliance with the Elastic License +# 2.0. +# + +set -e +./check_env_variables.sh + + +LIST_ID=${1:-endpoint_list} +NAMESPACE_TYPE=${2-agnostic} + +# First, post a exception list and two list items for the example to work +# ./post_exception_list.sh ./exception_lists/new/exception_list_agnostic.json +# ./post_exception_list_item.sh ./exception_lists/new/exception_list_item_agnostic.json + +# Retrieve exception list stats by os +# Example: ./summary_exception_list.sh endpoint_list agnostic +curl -s -k \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET "${KIBANA_URL}${SPACE_URL}/api/exception_lists/summary?list_id=${LIST_ID}&namespace_type=${NAMESPACE_TYPE}" | jq .