From 32200af4e93368a020549417e5ff2448275ec12b Mon Sep 17 00:00:00 2001 From: Bohdan Tsymbala Date: Wed, 2 Dec 2020 14:47:56 +0100 Subject: [PATCH 01/40] Changed the translation text for the description text in the antivirus registration form (#84626) * Changed the text for the description text in the antivirus registration form. Moved the form component to components folder and extracted translations into constants to make code more readable. * Extracted EventsForm to reduce duplication among events forms. --- .../antivirus_registration_form}/index.tsx | 49 +++-- .../view/components/events_form/index.tsx | 87 ++++++++ .../pages/policy/view/policy_details.tsx | 2 +- .../view/policy_forms/events/checkbox.tsx | 55 ----- .../policy/view/policy_forms/events/linux.tsx | 129 ++++------- .../policy/view/policy_forms/events/mac.tsx | 125 ++++------- .../view/policy_forms/events/translations.ts | 28 --- .../view/policy_forms/events/windows.tsx | 205 +++++++----------- 8 files changed, 281 insertions(+), 399 deletions(-) rename x-pack/plugins/security_solution/public/management/pages/policy/view/{policy_forms/antivirus_registration => components/antivirus_registration_form}/index.tsx (60%) create mode 100644 x-pack/plugins/security_solution/public/management/pages/policy/view/components/events_form/index.tsx delete mode 100644 x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/checkbox.tsx delete mode 100644 x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/translations.ts diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/antivirus_registration/index.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/components/antivirus_registration_form/index.tsx similarity index 60% rename from x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/antivirus_registration/index.tsx rename to x-pack/plugins/security_solution/public/management/pages/policy/view/components/antivirus_registration_form/index.tsx index 21fe14df81dd2..072f588663c5a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/antivirus_registration/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/components/antivirus_registration_form/index.tsx @@ -14,6 +14,29 @@ import { isAntivirusRegistrationEnabled } from '../../../store/policy_details/se import { usePolicyDetailsSelector } from '../../policy_hooks'; import { ConfigForm } from '../../components/config_form'; +const TRANSLATIONS: Readonly<{ [K in 'title' | 'description' | 'label']: string }> = { + title: i18n.translate( + 'xpack.securitySolution.endpoint.policy.details.antivirusRegistration.type', + { + defaultMessage: 'Register as anti-virus', + } + ), + description: i18n.translate( + 'xpack.securitySolution.endpoint.policy.details.antivirusRegistration.explanation', + { + defaultMessage: + 'Toggle on to register Elastic as an official Anti-Virus solution for Windows OS. ' + + 'This will also disable Windows Defender.', + } + ), + label: i18n.translate( + 'xpack.securitySolution.endpoint.policy.details.antivirusRegistration.toggle', + { + defaultMessage: 'Register as anti-virus', + } + ), +}; + export const AntivirusRegistrationForm = memo(() => { const antivirusRegistrationEnabled = usePolicyDetailsSelector(isAntivirusRegistrationEnabled); const dispatch = useDispatch(); @@ -30,31 +53,11 @@ export const AntivirusRegistrationForm = memo(() => { ); return ( - - - {i18n.translate( - 'xpack.securitySolution.endpoint.policy.details.antivirusRegistration.explanation', - { - defaultMessage: 'Switch the toggle to on to register Elastic anti-virus', - } - )} - + + {TRANSLATIONS.description} diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/components/events_form/index.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/components/events_form/index.tsx new file mode 100644 index 0000000000000..7cdf54316e4e2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/components/events_form/index.tsx @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiCheckbox, EuiSpacer, EuiText, htmlIdGenerator } from '@elastic/eui'; +import { OperatingSystem, UIPolicyConfig } from '../../../../../../../common/endpoint/types'; +import { OS } from '../../../types'; +import { ConfigForm, ConfigFormHeading } from '../../components/config_form'; + +const OPERATING_SYSTEM_TO_TEST_SUBJ: { [K in OperatingSystem]: string } = { + [OperatingSystem.WINDOWS]: 'Windows', + [OperatingSystem.LINUX]: 'Linux', + [OperatingSystem.MAC]: 'Mac', +}; + +interface OperatingSystemToOsMap { + [OperatingSystem.WINDOWS]: OS.windows; + [OperatingSystem.LINUX]: OS.linux; + [OperatingSystem.MAC]: OS.mac; +} + +export type ProtectionField< + T extends OperatingSystem +> = keyof UIPolicyConfig[OperatingSystemToOsMap[T]]['events']; + +export type EventFormSelection = { [K in ProtectionField]: boolean }; + +export interface EventFormOption { + name: string; + protectionField: ProtectionField; +} + +export interface EventsFormProps { + os: T; + options: ReadonlyArray>; + selection: EventFormSelection; + onValueSelection: (value: ProtectionField, selected: boolean) => void; +} + +const countSelected = (selection: EventFormSelection) => { + return Object.values(selection).filter((value) => value).length; +}; + +export const EventsForm = ({ + os, + options, + selection, + onValueSelection, +}: EventsFormProps) => ( + + {i18n.translate('xpack.securitySolution.endpoint.policy.details.eventCollectionsEnabled', { + defaultMessage: '{selected} / {total} event collections enabled', + values: { selected: countSelected(selection), total: options.length }, + })} + + } + > + + {i18n.translate('xpack.securitySolution.endpoint.policyDetailsConfig.eventingEvents', { + defaultMessage: 'Events', + })} + + + {options.map(({ name, protectionField }) => ( + onValueSelection(protectionField, event.target.checked)} + /> + ))} + +); + +EventsForm.displayName = 'EventsForm'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx index 9c11bc6f5a4d1..1ce099c494cf0 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx @@ -36,7 +36,7 @@ import { AgentsSummary } from './agents_summary'; import { VerticalDivider } from './vertical_divider'; import { WindowsEvents, MacEvents, LinuxEvents } from './policy_forms/events'; import { MalwareProtections } from './policy_forms/protections/malware'; -import { AntivirusRegistrationForm } from './policy_forms/antivirus_registration'; +import { AntivirusRegistrationForm } from './components/antivirus_registration_form'; import { useToasts } from '../../../../common/lib/kibana'; import { AppAction } from '../../../../common/store/actions'; import { SpyRoute } from '../../../../common/utils/route/spy_routes'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/checkbox.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/checkbox.tsx deleted file mode 100644 index 76077831c670b..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/checkbox.tsx +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { useCallback, useMemo } from 'react'; -import { EuiCheckbox, EuiCheckboxProps, htmlIdGenerator } from '@elastic/eui'; -import { useDispatch } from 'react-redux'; -import { usePolicyDetailsSelector } from '../../policy_hooks'; -import { policyConfig } from '../../../store/policy_details/selectors'; -import { PolicyDetailsAction } from '../../../store/policy_details'; -import { UIPolicyConfig } from '../../../../../../../common/endpoint/types'; - -type EventsCheckboxProps = Omit & { - name: string; - setter: (config: UIPolicyConfig, checked: boolean) => UIPolicyConfig; - getter: (config: UIPolicyConfig) => boolean; -}; - -export const EventsCheckbox = React.memo(function ({ - name, - setter, - getter, - ...otherProps -}: EventsCheckboxProps) { - const policyDetailsConfig = usePolicyDetailsSelector(policyConfig); - const selected = getter(policyDetailsConfig); - const dispatch = useDispatch<(action: PolicyDetailsAction) => void>(); - const checkboxId = useMemo(() => htmlIdGenerator()(), []); - - const handleCheckboxChange = useCallback( - (event: React.ChangeEvent) => { - if (policyDetailsConfig) { - dispatch({ - type: 'userChangedPolicyConfig', - payload: { policyConfig: setter(policyDetailsConfig, event.target.checked) }, - }); - } - }, - [dispatch, policyDetailsConfig, setter] - ); - - return ( - - ); -}); - -EventsCheckbox.displayName = 'EventsCheckbox'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/linux.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/linux.tsx index 999e3bac5653a..f9532eaecf701 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/linux.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/linux.tsx @@ -4,96 +4,59 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useMemo } from 'react'; +import React, { memo } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiSpacer, EuiText } from '@elastic/eui'; -import { EventsCheckbox } from './checkbox'; -import { OS } from '../../../types'; +import { useDispatch } from 'react-redux'; +import { OperatingSystem } from '../../../../../../../common/endpoint/types'; +import { policyConfig } from '../../../store/policy_details/selectors'; +import { setIn } from '../../../models/policy_details_config'; import { usePolicyDetailsSelector } from '../../policy_hooks'; -import { selectedLinuxEvents, totalLinuxEvents } from '../../../store/policy_details/selectors'; -import { ConfigForm, ConfigFormHeading } from '../../components/config_form'; -import { getIn, setIn } from '../../../models/policy_details_config'; -import { OperatingSystem, UIPolicyConfig } from '../../../../../../../common/endpoint/types'; -import { - COLLECTIONS_ENABLED_MESSAGE, - EVENTS_FORM_TYPE_LABEL, - EVENTS_HEADING, -} from './translations'; +import { EventFormOption, EventsForm } from '../../components/events_form'; -export const LinuxEvents = React.memo(() => { - const selected = usePolicyDetailsSelector(selectedLinuxEvents); - const total = usePolicyDetailsSelector(totalLinuxEvents); - - const checkboxes = useMemo(() => { - const items: Array<{ - name: string; - os: 'linux'; - protectionField: keyof UIPolicyConfig['linux']['events']; - }> = [ - { - name: i18n.translate( - 'xpack.securitySolution.endpoint.policyDetailsConfig.linux.events.file', - { - defaultMessage: 'File', - } - ), - os: OS.linux, - protectionField: 'file', - }, +const OPTIONS: ReadonlyArray> = [ + { + name: i18n.translate('xpack.securitySolution.endpoint.policyDetailsConfig.linux.events.file', { + defaultMessage: 'File', + }), + protectionField: 'file', + }, + { + name: i18n.translate( + 'xpack.securitySolution.endpoint.policyDetailsConfig.linux.events.process', { - name: i18n.translate( - 'xpack.securitySolution.endpoint.policyDetailsConfig.linux.events.process', - { - defaultMessage: 'Process', - } - ), - os: OS.linux, - protectionField: 'process', - }, + defaultMessage: 'Process', + } + ), + protectionField: 'process', + }, + { + name: i18n.translate( + 'xpack.securitySolution.endpoint.policyDetailsConfig.linux.events.network', { - name: i18n.translate( - 'xpack.securitySolution.endpoint.policyDetailsConfig.linux.events.network', - { - defaultMessage: 'Network', - } - ), - os: OS.linux, - protectionField: 'network', - }, - ]; - return ( - <> - {EVENTS_HEADING} - - {items.map((item, index) => { - return ( - - setIn(config)(item.os)('events')(item.protectionField)(checked) - } - getter={(config) => getIn(config)(item.os)('events')(item.protectionField)} - /> - ); - })} - - ); - }, []); + defaultMessage: 'Network', + } + ), + protectionField: 'network', + }, +]; + +export const LinuxEvents = memo(() => { + const policyDetailsConfig = usePolicyDetailsSelector(policyConfig); + const dispatch = useDispatch(); return ( - - {COLLECTIONS_ENABLED_MESSAGE(selected, total)} - + + os={OperatingSystem.LINUX} + selection={policyDetailsConfig.linux.events} + options={OPTIONS} + onValueSelection={(value, selected) => + dispatch({ + type: 'userChangedPolicyConfig', + payload: { policyConfig: setIn(policyDetailsConfig)('linux')('events')(value)(selected) }, + }) } - > - {checkboxes} - + /> ); }); + +LinuxEvents.displayName = 'LinuxEvents'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/mac.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/mac.tsx index 6e15a3c4cd43b..ac6ae531ba172 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/mac.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/mac.tsx @@ -4,96 +4,53 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useMemo } from 'react'; +import React, { memo } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiSpacer, EuiText } from '@elastic/eui'; -import { EventsCheckbox } from './checkbox'; -import { OS } from '../../../types'; +import { useDispatch } from 'react-redux'; +import { OperatingSystem } from '../../../../../../../common/endpoint/types'; +import { policyConfig } from '../../../store/policy_details/selectors'; +import { setIn } from '../../../models/policy_details_config'; import { usePolicyDetailsSelector } from '../../policy_hooks'; -import { selectedMacEvents, totalMacEvents } from '../../../store/policy_details/selectors'; -import { ConfigForm, ConfigFormHeading } from '../../components/config_form'; -import { getIn, setIn } from '../../../models/policy_details_config'; -import { OperatingSystem, UIPolicyConfig } from '../../../../../../../common/endpoint/types'; -import { - COLLECTIONS_ENABLED_MESSAGE, - EVENTS_FORM_TYPE_LABEL, - EVENTS_HEADING, -} from './translations'; +import { EventFormOption, EventsForm } from '../../components/events_form'; -export const MacEvents = React.memo(() => { - const selected = usePolicyDetailsSelector(selectedMacEvents); - const total = usePolicyDetailsSelector(totalMacEvents); +const OPTIONS: ReadonlyArray> = [ + { + name: i18n.translate('xpack.securitySolution.endpoint.policyDetailsConfig.mac.events.file', { + defaultMessage: 'File', + }), + protectionField: 'file', + }, + { + name: i18n.translate('xpack.securitySolution.endpoint.policyDetailsConfig.mac.events.process', { + defaultMessage: 'Process', + }), + protectionField: 'process', + }, + { + name: i18n.translate('xpack.securitySolution.endpoint.policyDetailsConfig.mac.events.network', { + defaultMessage: 'Network', + }), + protectionField: 'network', + }, +]; - const checkboxes = useMemo(() => { - const items: Array<{ - name: string; - os: 'mac'; - protectionField: keyof UIPolicyConfig['mac']['events']; - }> = [ - { - name: i18n.translate( - 'xpack.securitySolution.endpoint.policyDetailsConfig.mac.events.file', - { - defaultMessage: 'File', - } - ), - os: OS.mac, - protectionField: 'file', - }, - { - name: i18n.translate( - 'xpack.securitySolution.endpoint.policyDetailsConfig.mac.events.process', - { - defaultMessage: 'Process', - } - ), - os: OS.mac, - protectionField: 'process', - }, - { - name: i18n.translate( - 'xpack.securitySolution.endpoint.policyDetailsConfig.mac.events.network', - { - defaultMessage: 'Network', - } - ), - os: OS.mac, - protectionField: 'network', - }, - ]; - return ( - <> - {EVENTS_HEADING} - - {items.map((item, index) => { - return ( - - setIn(config)(item.os)('events')(item.protectionField)(checked) - } - getter={(config) => getIn(config)(item.os)('events')(item.protectionField)} - /> - ); - })} - - ); - }, []); +export const MacEvents = memo(() => { + const policyDetailsConfig = usePolicyDetailsSelector(policyConfig); + const dispatch = useDispatch(); return ( - - {COLLECTIONS_ENABLED_MESSAGE(selected, total)} - + + os={OperatingSystem.MAC} + selection={policyDetailsConfig.mac.events} + options={OPTIONS} + onValueSelection={(value, selected) => + dispatch({ + type: 'userChangedPolicyConfig', + payload: { policyConfig: setIn(policyDetailsConfig)('mac')('events')(value)(selected) }, + }) } - > - {checkboxes} - + /> ); }); + +MacEvents.displayName = 'MacEvents'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/translations.ts b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/translations.ts deleted file mode 100644 index 3b48b7969a8ce..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/translations.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; - -export const EVENTS_HEADING = i18n.translate( - 'xpack.securitySolution.endpoint.policyDetailsConfig.eventingEvents', - { - defaultMessage: 'Events', - } -); - -export const EVENTS_FORM_TYPE_LABEL = i18n.translate( - 'xpack.securitySolution.endpoint.policy.details.eventCollection', - { - defaultMessage: 'Event Collection', - } -); - -export const COLLECTIONS_ENABLED_MESSAGE = (selected: number, total: number) => { - return i18n.translate('xpack.securitySolution.endpoint.policy.details.eventCollectionsEnabled', { - defaultMessage: '{selected} / {total} event collections enabled', - values: { selected, total }, - }); -}; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/windows.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/windows.tsx index c381249cf24b9..c99f2a6b72ac3 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/windows.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/windows.tsx @@ -4,142 +4,97 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useMemo } from 'react'; +import React, { memo } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiSpacer, EuiText } from '@elastic/eui'; -import { EventsCheckbox } from './checkbox'; -import { OS } from '../../../types'; +import { useDispatch } from 'react-redux'; +import { OperatingSystem } from '../../../../../../../common/endpoint/types'; +import { policyConfig } from '../../../store/policy_details/selectors'; +import { setIn } from '../../../models/policy_details_config'; import { usePolicyDetailsSelector } from '../../policy_hooks'; -import { selectedWindowsEvents, totalWindowsEvents } from '../../../store/policy_details/selectors'; -import { ConfigForm, ConfigFormHeading } from '../../components/config_form'; -import { getIn, setIn } from '../../../models/policy_details_config'; -import { - Immutable, - OperatingSystem, - UIPolicyConfig, -} from '../../../../../../../common/endpoint/types'; -import { - COLLECTIONS_ENABLED_MESSAGE, - EVENTS_FORM_TYPE_LABEL, - EVENTS_HEADING, -} from './translations'; +import { EventFormOption, EventsForm } from '../../components/events_form'; -export const WindowsEvents = React.memo(() => { - const selected = usePolicyDetailsSelector(selectedWindowsEvents); - const total = usePolicyDetailsSelector(totalWindowsEvents); - - const checkboxes = useMemo(() => { - const items: Immutable< - Array<{ - name: string; - os: 'windows'; - protectionField: keyof UIPolicyConfig['windows']['events']; - }> - > = [ - { - name: i18n.translate( - 'xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.dllDriverLoad', - { - defaultMessage: 'DLL and Driver Load', - } - ), - os: OS.windows, - protectionField: 'dll_and_driver_load', - }, +const OPTIONS: ReadonlyArray> = [ + { + name: i18n.translate( + 'xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.dllDriverLoad', { - name: i18n.translate( - 'xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.dns', - { - defaultMessage: 'DNS', - } - ), - os: OS.windows, - protectionField: 'dns', - }, + defaultMessage: 'DLL and Driver Load', + } + ), + protectionField: 'dll_and_driver_load', + }, + { + name: i18n.translate('xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.dns', { + defaultMessage: 'DNS', + }), + protectionField: 'dns', + }, + { + name: i18n.translate( + 'xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.file', { - name: i18n.translate( - 'xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.file', - { - defaultMessage: 'File', - } - ), - os: OS.windows, - protectionField: 'file', - }, + defaultMessage: 'File', + } + ), + protectionField: 'file', + }, + { + name: i18n.translate( + 'xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.network', { - name: i18n.translate( - 'xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.network', - { - defaultMessage: 'Network', - } - ), - os: OS.windows, - protectionField: 'network', - }, + defaultMessage: 'Network', + } + ), + protectionField: 'network', + }, + { + name: i18n.translate( + 'xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.process', { - name: i18n.translate( - 'xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.process', - { - defaultMessage: 'Process', - } - ), - os: OS.windows, - protectionField: 'process', - }, + defaultMessage: 'Process', + } + ), + protectionField: 'process', + }, + { + name: i18n.translate( + 'xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.registry', { - name: i18n.translate( - 'xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.registry', - { - defaultMessage: 'Registry', - } - ), - os: OS.windows, - protectionField: 'registry', - }, + defaultMessage: 'Registry', + } + ), + protectionField: 'registry', + }, + { + name: i18n.translate( + 'xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.security', { - name: i18n.translate( - 'xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.security', - { - defaultMessage: 'Security', - } - ), - os: OS.windows, - protectionField: 'security', - }, - ]; - return ( - <> - {EVENTS_HEADING} - - {items.map((item, index) => { - return ( - - setIn(config)(item.os)('events')(item.protectionField)(checked) - } - getter={(config) => getIn(config)(item.os)('events')(item.protectionField)} - /> - ); - })} - - ); - }, []); + defaultMessage: 'Security', + } + ), + protectionField: 'security', + }, +]; + +export const WindowsEvents = memo(() => { + const policyDetailsConfig = usePolicyDetailsSelector(policyConfig); + const dispatch = useDispatch(); return ( - - {COLLECTIONS_ENABLED_MESSAGE(selected, total)} - + + os={OperatingSystem.WINDOWS} + selection={policyDetailsConfig.windows.events} + options={OPTIONS} + onValueSelection={(value, selected) => + dispatch({ + type: 'userChangedPolicyConfig', + payload: { + policyConfig: setIn(policyDetailsConfig)('windows')('events')(value)(selected), + }, + }) } - > - {checkboxes} - + /> ); }); + +WindowsEvents.displayName = 'WindowsEvents'; From c1e7f69ca1bf3e9abe787fb1d2dfa5847a6ec73c Mon Sep 17 00:00:00 2001 From: Aleh Zasypkin Date: Wed, 2 Dec 2020 15:00:01 +0100 Subject: [PATCH 02/40] Migrate security routes to a new Elasticsearch client. (#84528) --- .../elasticsearch_client_plugin.ts | 254 ------------------ x-pack/plugins/security/server/plugin.ts | 1 - .../server/routes/api_keys/enabled.test.ts | 165 +++++------- .../server/routes/api_keys/get.test.ts | 27 +- .../security/server/routes/api_keys/get.ts | 10 +- .../server/routes/api_keys/invalidate.test.ts | 41 ++- .../server/routes/api_keys/invalidate.ts | 8 +- .../server/routes/api_keys/privileges.test.ts | 233 +++++----------- .../server/routes/api_keys/privileges.ts | 26 +- .../authorization/privileges/get_builtin.ts | 8 +- .../routes/authorization/roles/delete.test.ts | 30 +-- .../routes/authorization/roles/delete.ts | 4 +- .../routes/authorization/roles/get.test.ts | 30 +-- .../server/routes/authorization/roles/get.ts | 12 +- .../authorization/roles/get_all.test.ts | 27 +- .../routes/authorization/roles/get_all.ts | 10 +- .../routes/authorization/roles/put.test.ts | 182 ++++++------- .../server/routes/authorization/roles/put.ts | 21 +- .../security/server/routes/index.mock.ts | 2 - .../plugins/security/server/routes/index.ts | 9 +- .../server/routes/indices/get_fields.test.ts | 14 +- .../server/routes/indices/get_fields.ts | 16 +- .../server/routes/role_mapping/delete.test.ts | 52 ++-- .../server/routes/role_mapping/delete.ts | 14 +- .../routes/role_mapping/feature_check.test.ts | 92 +++---- .../routes/role_mapping/feature_check.ts | 46 ++-- .../server/routes/role_mapping/get.test.ts | 100 +++---- .../server/routes/role_mapping/get.ts | 12 +- .../server/routes/role_mapping/post.test.ts | 71 +++-- .../server/routes/role_mapping/post.ts | 15 +- .../routes/users/change_password.test.ts | 98 +++---- .../server/routes/users/change_password.ts | 45 ++-- .../server/routes/users/create_or_update.ts | 4 +- .../security/server/routes/users/delete.ts | 8 +- .../security/server/routes/users/get.ts | 12 +- .../security/server/routes/users/get_all.ts | 4 +- 36 files changed, 610 insertions(+), 1093 deletions(-) diff --git a/x-pack/plugins/security/server/elasticsearch/elasticsearch_client_plugin.ts b/x-pack/plugins/security/server/elasticsearch/elasticsearch_client_plugin.ts index 529e8a8aa6e9c..0a43d8dd6973a 100644 --- a/x-pack/plugins/security/server/elasticsearch/elasticsearch_client_plugin.ts +++ b/x-pack/plugins/security/server/elasticsearch/elasticsearch_client_plugin.ts @@ -22,133 +22,6 @@ export function elasticsearchClientPlugin(Client: any, config: unknown, componen }, }); - /** - * Perform a [shield.changePassword](Change the password of a user) request - * - * @param {Object} params - An object with parameters used to carry out this action - * @param {Boolean} params.refresh - Refresh the index after performing the operation - * @param {String} params.username - The username of the user to change the password for - */ - shield.changePassword = ca({ - params: { - refresh: { - type: 'boolean', - }, - }, - urls: [ - { - fmt: '/_security/user/<%=username%>/_password', - req: { - username: { - type: 'string', - required: false, - }, - }, - }, - { - fmt: '/_security/user/_password', - }, - ], - needBody: true, - method: 'POST', - }); - - /** - * Perform a [shield.clearCachedRealms](Clears the internal user caches for specified realms) request - * - * @param {Object} params - An object with parameters used to carry out this action - * @param {String} params.usernames - Comma-separated list of usernames to clear from the cache - * @param {String} params.realms - Comma-separated list of realms to clear - */ - shield.clearCachedRealms = ca({ - params: { - usernames: { - type: 'string', - required: false, - }, - }, - url: { - fmt: '/_security/realm/<%=realms%>/_clear_cache', - req: { - realms: { - type: 'string', - required: true, - }, - }, - }, - method: 'POST', - }); - - /** - * Perform a [shield.clearCachedRoles](Clears the internal caches for specified roles) request - * - * @param {Object} params - An object with parameters used to carry out this action - * @param {String} params.name - Role name - */ - shield.clearCachedRoles = ca({ - params: {}, - url: { - fmt: '/_security/role/<%=name%>/_clear_cache', - req: { - name: { - type: 'string', - required: true, - }, - }, - }, - method: 'POST', - }); - - /** - * Perform a [shield.deleteRole](Remove a role from the native shield realm) request - * - * @param {Object} params - An object with parameters used to carry out this action - * @param {Boolean} params.refresh - Refresh the index after performing the operation - * @param {String} params.name - Role name - */ - shield.deleteRole = ca({ - params: { - refresh: { - type: 'boolean', - }, - }, - url: { - fmt: '/_security/role/<%=name%>', - req: { - name: { - type: 'string', - required: true, - }, - }, - }, - method: 'DELETE', - }); - - /** - * Perform a [shield.deleteUser](Remove a user from the native shield realm) request - * - * @param {Object} params - An object with parameters used to carry out this action - * @param {Boolean} params.refresh - Refresh the index after performing the operation - * @param {String} params.username - username - */ - shield.deleteUser = ca({ - params: { - refresh: { - type: 'boolean', - }, - }, - url: { - fmt: '/_security/user/<%=username%>', - req: { - username: { - type: 'string', - required: true, - }, - }, - }, - method: 'DELETE', - }); - /** * Perform a [shield.getRole](Retrieve one or more roles from the native shield realm) request * @@ -173,30 +46,6 @@ export function elasticsearchClientPlugin(Client: any, config: unknown, componen ], }); - /** - * Perform a [shield.getUser](Retrieve one or more users from the native shield realm) request - * - * @param {Object} params - An object with parameters used to carry out this action - * @param {String, String[], Boolean} params.username - A comma-separated list of usernames - */ - shield.getUser = ca({ - params: {}, - urls: [ - { - fmt: '/_security/user/<%=username%>', - req: { - username: { - type: 'list', - required: false, - }, - }, - }, - { - fmt: '/_security/user', - }, - ], - }); - /** * Perform a [shield.putRole](Update or create a role for the native shield realm) request * @@ -249,19 +98,6 @@ export function elasticsearchClientPlugin(Client: any, config: unknown, componen method: 'PUT', }); - /** - * Perform a [shield.getUserPrivileges](Retrieve a user's list of privileges) request - * - */ - shield.getUserPrivileges = ca({ - params: {}, - urls: [ - { - fmt: '/_security/user/_privileges', - }, - ], - }); - /** * Asks Elasticsearch to prepare SAML authentication request to be sent to * the 3rd-party SAML identity provider. @@ -489,36 +325,6 @@ export function elasticsearchClientPlugin(Client: any, config: unknown, componen }, }); - shield.getBuiltinPrivileges = ca({ - params: {}, - urls: [ - { - fmt: '/_security/privilege/_builtin', - }, - ], - }); - - /** - * Gets API keys in Elasticsearch - * @param {boolean} owner A boolean flag that can be used to query API keys owned by the currently authenticated user. - * Defaults to false. The realm_name or username parameters cannot be specified when this parameter is set to true as - * they are assumed to be the currently authenticated ones. - */ - shield.getAPIKeys = ca({ - method: 'GET', - urls: [ - { - fmt: `/_security/api_key?owner=<%=owner%>`, - req: { - owner: { - type: 'boolean', - required: true, - }, - }, - }, - ], - }); - /** * Creates an API key in Elasticsearch for the current user. * @@ -591,64 +397,4 @@ export function elasticsearchClientPlugin(Client: any, config: unknown, componen fmt: '/_security/delegate_pki', }, }); - - /** - * Retrieves all configured role mappings. - * - * @returns {{ [roleMappingName]: { enabled: boolean; roles: string[]; rules: Record} }} - */ - shield.getRoleMappings = ca({ - method: 'GET', - urls: [ - { - fmt: '/_security/role_mapping', - }, - { - fmt: '/_security/role_mapping/<%=name%>', - req: { - name: { - type: 'string', - required: true, - }, - }, - }, - ], - }); - - /** - * Saves the specified role mapping. - */ - shield.saveRoleMapping = ca({ - method: 'POST', - needBody: true, - urls: [ - { - fmt: '/_security/role_mapping/<%=name%>', - req: { - name: { - type: 'string', - required: true, - }, - }, - }, - ], - }); - - /** - * Deletes the specified role mapping. - */ - shield.deleteRoleMapping = ca({ - method: 'DELETE', - urls: [ - { - fmt: '/_security/role_mapping/<%=name%>', - req: { - name: { - type: 'string', - required: true, - }, - }, - }, - ], - }); } diff --git a/x-pack/plugins/security/server/plugin.ts b/x-pack/plugins/security/server/plugin.ts index 17f2480026cc7..d6fe1356ce145 100644 --- a/x-pack/plugins/security/server/plugin.ts +++ b/x-pack/plugins/security/server/plugin.ts @@ -230,7 +230,6 @@ export class Plugin { basePath: core.http.basePath, httpResources: core.http.resources, logger: this.initializerContext.logger.get('routes'), - clusterClient, config, authc: this.authc, authz, diff --git a/x-pack/plugins/security/server/routes/api_keys/enabled.test.ts b/x-pack/plugins/security/server/routes/api_keys/enabled.test.ts index 7968402cd2176..3a22b9fe003a1 100644 --- a/x-pack/plugins/security/server/routes/api_keys/enabled.test.ts +++ b/x-pack/plugins/security/server/routes/api_keys/enabled.test.ts @@ -4,115 +4,96 @@ * you may not use this file except in compliance with the Elastic License. */ -import { kibanaResponseFactory, RequestHandlerContext } from '../../../../../../src/core/server'; -import { LicenseCheck } from '../../../../licensing/server'; +import Boom from '@hapi/boom'; +import { + kibanaResponseFactory, + RequestHandler, + RequestHandlerContext, +} from '../../../../../../src/core/server'; import { httpServerMock } from '../../../../../../src/core/server/mocks'; import { routeDefinitionParamsMock } from '../index.mock'; -import Boom from '@hapi/boom'; -import { defineEnabledApiKeysRoutes } from './enabled'; -import { APIKeys } from '../../authentication/api_keys'; -interface TestOptions { - licenseCheckResult?: LicenseCheck; - apiResponse?: () => Promise; - asserts: { statusCode: number; result?: Record }; -} +import { defineEnabledApiKeysRoutes } from './enabled'; +import { Authentication } from '../../authentication'; describe('API keys enabled', () => { - const enabledApiKeysTest = ( - description: string, - { licenseCheckResult = { state: 'valid' }, apiResponse, asserts }: TestOptions - ) => { - test(description, async () => { - const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); - - const apiKeys = new APIKeys({ - logger: mockRouteDefinitionParams.logger, - clusterClient: mockRouteDefinitionParams.clusterClient, - license: mockRouteDefinitionParams.license, - }); - - mockRouteDefinitionParams.authc.areAPIKeysEnabled.mockImplementation(() => - apiKeys.areAPIKeysEnabled() + function getMockContext( + licenseCheckResult: { state: string; message?: string } = { state: 'valid' } + ) { + return ({ + licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } }, + } as unknown) as RequestHandlerContext; + } + + let routeHandler: RequestHandler; + let authc: jest.Mocked; + beforeEach(() => { + const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); + authc = mockRouteDefinitionParams.authc; + + defineEnabledApiKeysRoutes(mockRouteDefinitionParams); + + const [, apiKeyRouteHandler] = mockRouteDefinitionParams.router.get.mock.calls.find( + ([{ path }]) => path === '/internal/security/api_key/_enabled' + )!; + routeHandler = apiKeyRouteHandler; + }); + + describe('failure', () => { + test('returns result of license checker', async () => { + const mockContext = getMockContext({ state: 'invalid', message: 'test forbidden message' }); + const response = await routeHandler( + mockContext, + httpServerMock.createKibanaRequest(), + kibanaResponseFactory ); - if (apiResponse) { - mockRouteDefinitionParams.clusterClient.callAsInternalUser.mockImplementation(apiResponse); - } - - defineEnabledApiKeysRoutes(mockRouteDefinitionParams); - const [[, handler]] = mockRouteDefinitionParams.router.get.mock.calls; - - const headers = { authorization: 'foo' }; - const mockRequest = httpServerMock.createKibanaRequest({ - method: 'get', - path: '/internal/security/api_key/_enabled', - headers, - }); - const mockContext = ({ - licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } }, - } as unknown) as RequestHandlerContext; - - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); - expect(response.status).toBe(asserts.statusCode); - expect(response.payload).toEqual(asserts.result); - - if (apiResponse) { - expect(mockRouteDefinitionParams.clusterClient.callAsInternalUser).toHaveBeenCalledWith( - 'shield.invalidateAPIKey', - { - body: { - id: expect.any(String), - }, - } - ); - } else { - expect(mockRouteDefinitionParams.clusterClient.asScoped).not.toHaveBeenCalled(); - } + expect(response.status).toBe(403); + expect(response.payload).toEqual({ message: 'test forbidden message' }); expect(mockContext.licensing.license.check).toHaveBeenCalledWith('security', 'basic'); }); - }; - describe('failure', () => { - enabledApiKeysTest('returns result of license checker', { - licenseCheckResult: { state: 'invalid', message: 'test forbidden message' }, - asserts: { statusCode: 403, result: { message: 'test forbidden message' } }, - }); + test('returns error from cluster client', async () => { + const error = Boom.notAcceptable('test not acceptable message'); + authc.areAPIKeysEnabled.mockRejectedValue(error); + + const response = await routeHandler( + getMockContext(), + httpServerMock.createKibanaRequest(), + kibanaResponseFactory + ); - const error = Boom.notAcceptable('test not acceptable message'); - enabledApiKeysTest('returns error from cluster client', { - apiResponse: async () => { - throw error; - }, - asserts: { statusCode: 406, result: error }, + expect(response.status).toBe(406); + expect(response.payload).toEqual(error); }); }); describe('success', () => { - enabledApiKeysTest('returns true if API Keys are enabled', { - apiResponse: async () => ({}), - asserts: { - statusCode: 200, - result: { - apiKeysEnabled: true, - }, - }, + test('returns true if API Keys are enabled', async () => { + authc.areAPIKeysEnabled.mockResolvedValue(true); + + const response = await routeHandler( + getMockContext(), + httpServerMock.createKibanaRequest(), + kibanaResponseFactory + ); + + expect(response.status).toBe(200); + expect(response.payload).toEqual({ apiKeysEnabled: true }); }); - enabledApiKeysTest('returns false if API Keys are disabled', { - apiResponse: async () => { - const error = new Error(); - (error as any).body = { - error: { 'disabled.feature': 'api_keys' }, - }; - throw error; - }, - asserts: { - statusCode: 200, - result: { - apiKeysEnabled: false, - }, - }, + + test('returns false if API Keys are disabled', async () => { + authc.areAPIKeysEnabled.mockResolvedValue(false); + + const response = await routeHandler( + getMockContext(), + httpServerMock.createKibanaRequest(), + kibanaResponseFactory + ); + + expect(response.status).toBe(200); + expect(response.payload).toEqual({ apiKeysEnabled: false }); }); }); }); diff --git a/x-pack/plugins/security/server/routes/api_keys/get.test.ts b/x-pack/plugins/security/server/routes/api_keys/get.test.ts index cb991fb2f5aac..cc9e9a68e6e36 100644 --- a/x-pack/plugins/security/server/routes/api_keys/get.test.ts +++ b/x-pack/plugins/security/server/routes/api_keys/get.test.ts @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { kibanaResponseFactory, RequestHandlerContext } from '../../../../../../src/core/server'; +import { kibanaResponseFactory } from '../../../../../../src/core/server'; import { LicenseCheck } from '../../../../licensing/server'; import { defineGetApiKeysRoutes } from './get'; -import { elasticsearchServiceMock, httpServerMock } from '../../../../../../src/core/server/mocks'; +import { httpServerMock, coreMock } from '../../../../../../src/core/server/mocks'; import { routeDefinitionParamsMock } from '../index.mock'; import Boom from '@hapi/boom'; @@ -26,11 +26,15 @@ describe('Get API keys', () => { ) => { test(description, async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); + const mockContext = { + core: coreMock.createRequestHandlerContext(), + licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } } as any, + }; - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockRouteDefinitionParams.clusterClient.asScoped.mockReturnValue(mockScopedClusterClient); if (apiResponse) { - mockScopedClusterClient.callAsCurrentUser.mockImplementation(apiResponse); + mockContext.core.elasticsearch.client.asCurrentUser.security.getApiKey.mockImplementation( + (async () => ({ body: await apiResponse() })) as any + ); } defineGetApiKeysRoutes(mockRouteDefinitionParams); @@ -43,22 +47,15 @@ describe('Get API keys', () => { query: { isAdmin: isAdmin.toString() }, headers, }); - const mockContext = ({ - licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } }, - } as unknown) as RequestHandlerContext; const response = await handler(mockContext, mockRequest, kibanaResponseFactory); expect(response.status).toBe(asserts.statusCode); expect(response.payload).toEqual(asserts.result); if (apiResponse) { - expect(mockRouteDefinitionParams.clusterClient.asScoped).toHaveBeenCalledWith(mockRequest); - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith( - 'shield.getAPIKeys', - { owner: !isAdmin } - ); - } else { - expect(mockScopedClusterClient.callAsCurrentUser).not.toHaveBeenCalled(); + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.getApiKey + ).toHaveBeenCalledWith({ owner: !isAdmin }); } expect(mockContext.licensing.license.check).toHaveBeenCalledWith('security', 'basic'); }); diff --git a/x-pack/plugins/security/server/routes/api_keys/get.ts b/x-pack/plugins/security/server/routes/api_keys/get.ts index 6e98b4b098405..b0c6ec090a0e5 100644 --- a/x-pack/plugins/security/server/routes/api_keys/get.ts +++ b/x-pack/plugins/security/server/routes/api_keys/get.ts @@ -10,7 +10,7 @@ import { wrapIntoCustomErrorResponse } from '../../errors'; import { createLicensedRouteHandler } from '../licensed_route_handler'; import { RouteDefinitionParams } from '..'; -export function defineGetApiKeysRoutes({ router, clusterClient }: RouteDefinitionParams) { +export function defineGetApiKeysRoutes({ router }: RouteDefinitionParams) { router.get( { path: '/internal/security/api_key', @@ -28,11 +28,11 @@ export function defineGetApiKeysRoutes({ router, clusterClient }: RouteDefinitio createLicensedRouteHandler(async (context, request, response) => { try { const isAdmin = request.query.isAdmin === 'true'; - const { api_keys: apiKeys } = (await clusterClient - .asScoped(request) - .callAsCurrentUser('shield.getAPIKeys', { owner: !isAdmin })) as { api_keys: ApiKey[] }; + const apiResponse = await context.core.elasticsearch.client.asCurrentUser.security.getApiKey<{ + api_keys: ApiKey[]; + }>({ owner: !isAdmin }); - const validKeys = apiKeys.filter(({ invalidated }) => !invalidated); + const validKeys = apiResponse.body.api_keys.filter(({ invalidated }) => !invalidated); return response.ok({ body: { apiKeys: validKeys } }); } catch (error) { diff --git a/x-pack/plugins/security/server/routes/api_keys/invalidate.test.ts b/x-pack/plugins/security/server/routes/api_keys/invalidate.test.ts index 88e52f735395d..9ac41fdfa7483 100644 --- a/x-pack/plugins/security/server/routes/api_keys/invalidate.test.ts +++ b/x-pack/plugins/security/server/routes/api_keys/invalidate.test.ts @@ -6,18 +6,18 @@ import Boom from '@hapi/boom'; import { Type } from '@kbn/config-schema'; -import { kibanaResponseFactory, RequestHandlerContext } from '../../../../../../src/core/server'; +import { kibanaResponseFactory } from '../../../../../../src/core/server'; import { LicenseCheck } from '../../../../licensing/server'; import { defineInvalidateApiKeysRoutes } from './invalidate'; -import { elasticsearchServiceMock, httpServerMock } from '../../../../../../src/core/server/mocks'; +import { coreMock, httpServerMock } from '../../../../../../src/core/server/mocks'; import { routeDefinitionParamsMock } from '../index.mock'; interface TestOptions { licenseCheckResult?: LicenseCheck; apiResponses?: Array<() => Promise>; payload?: Record; - asserts: { statusCode: number; result?: Record; apiArguments?: unknown[][] }; + asserts: { statusCode: number; result?: Record; apiArguments?: unknown[] }; } describe('Invalidate API keys', () => { @@ -27,10 +27,15 @@ describe('Invalidate API keys', () => { ) => { test(description, async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockRouteDefinitionParams.clusterClient.asScoped.mockReturnValue(mockScopedClusterClient); + const mockContext = { + core: coreMock.createRequestHandlerContext(), + licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } } as any, + }; + for (const apiResponse of apiResponses) { - mockScopedClusterClient.callAsCurrentUser.mockImplementationOnce(apiResponse); + mockContext.core.elasticsearch.client.asCurrentUser.security.invalidateApiKey.mockImplementationOnce( + (async () => ({ body: await apiResponse() })) as any + ); } defineInvalidateApiKeysRoutes(mockRouteDefinitionParams); @@ -43,9 +48,6 @@ describe('Invalidate API keys', () => { body: payload !== undefined ? (validate as any).body.validate(payload) : undefined, headers, }); - const mockContext = ({ - licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } }, - } as unknown) as RequestHandlerContext; const response = await handler(mockContext, mockRequest, kibanaResponseFactory); expect(response.status).toBe(asserts.statusCode); @@ -53,13 +55,10 @@ describe('Invalidate API keys', () => { if (Array.isArray(asserts.apiArguments)) { for (const apiArguments of asserts.apiArguments) { - expect(mockRouteDefinitionParams.clusterClient.asScoped).toHaveBeenCalledWith( - mockRequest - ); - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith(...apiArguments); + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.invalidateApiKey + ).toHaveBeenCalledWith(apiArguments); } - } else { - expect(mockScopedClusterClient.callAsCurrentUser).not.toHaveBeenCalled(); } expect(mockContext.licensing.license.check).toHaveBeenCalledWith('security', 'basic'); }); @@ -128,7 +127,7 @@ describe('Invalidate API keys', () => { isAdmin: true, }, asserts: { - apiArguments: [['shield.invalidateAPIKey', { body: { id: 'si8If24B1bKsmSLTAhJV' } }]], + apiArguments: [{ body: { id: 'si8If24B1bKsmSLTAhJV' } }], statusCode: 200, result: { itemsInvalidated: [], @@ -152,7 +151,7 @@ describe('Invalidate API keys', () => { isAdmin: true, }, asserts: { - apiArguments: [['shield.invalidateAPIKey', { body: { id: 'si8If24B1bKsmSLTAhJV' } }]], + apiArguments: [{ body: { id: 'si8If24B1bKsmSLTAhJV' } }], statusCode: 200, result: { itemsInvalidated: [{ id: 'si8If24B1bKsmSLTAhJV', name: 'my-api-key' }], @@ -168,9 +167,7 @@ describe('Invalidate API keys', () => { isAdmin: false, }, asserts: { - apiArguments: [ - ['shield.invalidateAPIKey', { body: { id: 'si8If24B1bKsmSLTAhJV', owner: true } }], - ], + apiArguments: [{ body: { id: 'si8If24B1bKsmSLTAhJV', owner: true } }], statusCode: 200, result: { itemsInvalidated: [{ id: 'si8If24B1bKsmSLTAhJV', name: 'my-api-key' }], @@ -195,8 +192,8 @@ describe('Invalidate API keys', () => { }, asserts: { apiArguments: [ - ['shield.invalidateAPIKey', { body: { id: 'si8If24B1bKsmSLTAhJV' } }], - ['shield.invalidateAPIKey', { body: { id: 'ab8If24B1bKsmSLTAhNC' } }], + { body: { id: 'si8If24B1bKsmSLTAhJV' } }, + { body: { id: 'ab8If24B1bKsmSLTAhNC' } }, ], statusCode: 200, result: { diff --git a/x-pack/plugins/security/server/routes/api_keys/invalidate.ts b/x-pack/plugins/security/server/routes/api_keys/invalidate.ts index dd472c0b60cbc..3977954197007 100644 --- a/x-pack/plugins/security/server/routes/api_keys/invalidate.ts +++ b/x-pack/plugins/security/server/routes/api_keys/invalidate.ts @@ -15,7 +15,7 @@ interface ResponseType { errors: Array & { error: Error }>; } -export function defineInvalidateApiKeysRoutes({ router, clusterClient }: RouteDefinitionParams) { +export function defineInvalidateApiKeysRoutes({ router }: RouteDefinitionParams) { router.post( { path: '/internal/security/api_key/invalidate', @@ -28,8 +28,6 @@ export function defineInvalidateApiKeysRoutes({ router, clusterClient }: RouteDe }, createLicensedRouteHandler(async (context, request, response) => { try { - const scopedClusterClient = clusterClient.asScoped(request); - // Invalidate all API keys in parallel. const invalidationResult = ( await Promise.all( @@ -41,7 +39,9 @@ export function defineInvalidateApiKeysRoutes({ router, clusterClient }: RouteDe } // Send the request to invalidate the API key and return an error if it could not be deleted. - await scopedClusterClient.callAsCurrentUser('shield.invalidateAPIKey', { body }); + await context.core.elasticsearch.client.asCurrentUser.security.invalidateApiKey({ + body, + }); return { key, error: undefined }; } catch (error) { return { key, error: wrapError(error) }; diff --git a/x-pack/plugins/security/server/routes/api_keys/privileges.test.ts b/x-pack/plugins/security/server/routes/api_keys/privileges.test.ts index ecc3d32e20aec..b06d1329dc1db 100644 --- a/x-pack/plugins/security/server/routes/api_keys/privileges.test.ts +++ b/x-pack/plugins/security/server/routes/api_keys/privileges.test.ts @@ -6,23 +6,17 @@ import Boom from '@hapi/boom'; import { LicenseCheck } from '../../../../licensing/server'; -import { kibanaResponseFactory, RequestHandlerContext } from '../../../../../../src/core/server'; +import { kibanaResponseFactory } from '../../../../../../src/core/server'; -import { elasticsearchServiceMock, httpServerMock } from '../../../../../../src/core/server/mocks'; +import { coreMock, httpServerMock } from '../../../../../../src/core/server/mocks'; import { routeDefinitionParamsMock } from '../index.mock'; import { defineCheckPrivilegesRoutes } from './privileges'; -import { APIKeys } from '../../authentication/api_keys'; interface TestOptions { licenseCheckResult?: LicenseCheck; - callAsInternalUserResponses?: Array<() => Promise>; - callAsCurrentUserResponses?: Array<() => Promise>; - asserts: { - statusCode: number; - result?: Record; - callAsInternalUserAPIArguments?: unknown[][]; - callAsCurrentUserAPIArguments?: unknown[][]; - }; + areAPIKeysEnabled?: boolean; + apiResponse?: () => Promise; + asserts: { statusCode: number; result?: Record; apiArguments?: unknown }; } describe('Check API keys privileges', () => { @@ -30,32 +24,23 @@ describe('Check API keys privileges', () => { description: string, { licenseCheckResult = { state: 'valid' }, - callAsInternalUserResponses = [], - callAsCurrentUserResponses = [], + areAPIKeysEnabled = true, + apiResponse, asserts, }: TestOptions ) => { test(description, async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); + const mockContext = { + core: coreMock.createRequestHandlerContext(), + licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } } as any, + }; - const apiKeys = new APIKeys({ - logger: mockRouteDefinitionParams.logger, - clusterClient: mockRouteDefinitionParams.clusterClient, - license: mockRouteDefinitionParams.license, - }); - - mockRouteDefinitionParams.authc.areAPIKeysEnabled.mockImplementation(() => - apiKeys.areAPIKeysEnabled() - ); + mockRouteDefinitionParams.authc.areAPIKeysEnabled.mockResolvedValue(areAPIKeysEnabled); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockRouteDefinitionParams.clusterClient.asScoped.mockReturnValue(mockScopedClusterClient); - for (const apiResponse of callAsCurrentUserResponses) { - mockScopedClusterClient.callAsCurrentUser.mockImplementationOnce(apiResponse); - } - for (const apiResponse of callAsInternalUserResponses) { - mockRouteDefinitionParams.clusterClient.callAsInternalUser.mockImplementationOnce( - apiResponse + if (apiResponse) { + mockContext.core.elasticsearch.client.asCurrentUser.security.hasPrivileges.mockImplementation( + (async () => ({ body: await apiResponse() })) as any ); } @@ -68,33 +53,15 @@ describe('Check API keys privileges', () => { path: '/internal/security/api_key/privileges', headers, }); - const mockContext = ({ - licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } }, - } as unknown) as RequestHandlerContext; const response = await handler(mockContext, mockRequest, kibanaResponseFactory); expect(response.status).toBe(asserts.statusCode); expect(response.payload).toEqual(asserts.result); - if (Array.isArray(asserts.callAsCurrentUserAPIArguments)) { - for (const apiArguments of asserts.callAsCurrentUserAPIArguments) { - expect(mockRouteDefinitionParams.clusterClient.asScoped).toHaveBeenCalledWith( - mockRequest - ); - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith(...apiArguments); - } - } else { - expect(mockScopedClusterClient.callAsCurrentUser).not.toHaveBeenCalled(); - } - - if (Array.isArray(asserts.callAsInternalUserAPIArguments)) { - for (const apiArguments of asserts.callAsInternalUserAPIArguments) { - expect(mockRouteDefinitionParams.clusterClient.callAsInternalUser).toHaveBeenCalledWith( - ...apiArguments - ); - } - } else { - expect(mockRouteDefinitionParams.clusterClient.callAsInternalUser).not.toHaveBeenCalled(); + if (asserts.apiArguments) { + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.hasPrivileges + ).toHaveBeenCalledWith(asserts.apiArguments); } expect(mockContext.licensing.license.check).toHaveBeenCalledWith('security', 'basic'); @@ -109,22 +76,13 @@ describe('Check API keys privileges', () => { const error = Boom.notAcceptable('test not acceptable message'); getPrivilegesTest('returns error from cluster client', { - callAsCurrentUserResponses: [ - async () => { - throw error; - }, - ], - callAsInternalUserResponses: [async () => {}], + apiResponse: async () => { + throw error; + }, asserts: { - callAsCurrentUserAPIArguments: [ - [ - 'shield.hasPrivileges', - { body: { cluster: ['manage_security', 'manage_api_key', 'manage_own_api_key'] } }, - ], - ], - callAsInternalUserAPIArguments: [ - ['shield.invalidateAPIKey', { body: { id: expect.any(String) } }], - ], + apiArguments: { + body: { cluster: ['manage_security', 'manage_api_key', 'manage_own_api_key'] }, + }, statusCode: 406, result: error, }, @@ -133,40 +91,17 @@ describe('Check API keys privileges', () => { describe('success', () => { getPrivilegesTest('returns areApiKeysEnabled and isAdmin', { - callAsCurrentUserResponses: [ - async () => ({ - username: 'elastic', - has_all_requested: true, - cluster: { manage_api_key: true, manage_security: true, manage_own_api_key: false }, - index: {}, - application: {}, - }), - ], - callAsInternalUserResponses: [ - async () => ({ - api_keys: [ - { - id: 'si8If24B1bKsmSLTAhJV', - name: 'my-api-key', - creation: 1574089261632, - expiration: 1574175661632, - invalidated: false, - username: 'elastic', - realm: 'reserved', - }, - ], - }), - ], + apiResponse: async () => ({ + username: 'elastic', + has_all_requested: true, + cluster: { manage_api_key: true, manage_security: true, manage_own_api_key: false }, + index: {}, + application: {}, + }), asserts: { - callAsCurrentUserAPIArguments: [ - [ - 'shield.hasPrivileges', - { body: { cluster: ['manage_security', 'manage_api_key', 'manage_own_api_key'] } }, - ], - ], - callAsInternalUserAPIArguments: [ - ['shield.invalidateAPIKey', { body: { id: expect.any(String) } }], - ], + apiArguments: { + body: { cluster: ['manage_security', 'manage_api_key', 'manage_own_api_key'] }, + }, statusCode: 200, result: { areApiKeysEnabled: true, isAdmin: true, canManage: true }, }, @@ -175,36 +110,18 @@ describe('Check API keys privileges', () => { getPrivilegesTest( 'returns areApiKeysEnabled=false when API Keys are disabled in Elasticsearch', { - callAsCurrentUserResponses: [ - async () => ({ - username: 'elastic', - has_all_requested: true, - cluster: { manage_api_key: true, manage_security: true, manage_own_api_key: true }, - index: {}, - application: {}, - }), - ], - callAsInternalUserResponses: [ - async () => { - const error = new Error(); - (error as any).body = { - error: { - 'disabled.feature': 'api_keys', - }, - }; - throw error; - }, - ], + apiResponse: async () => ({ + username: 'elastic', + has_all_requested: true, + cluster: { manage_api_key: true, manage_security: true, manage_own_api_key: true }, + index: {}, + application: {}, + }), + areAPIKeysEnabled: false, asserts: { - callAsCurrentUserAPIArguments: [ - [ - 'shield.hasPrivileges', - { body: { cluster: ['manage_security', 'manage_api_key', 'manage_own_api_key'] } }, - ], - ], - callAsInternalUserAPIArguments: [ - ['shield.invalidateAPIKey', { body: { id: expect.any(String) } }], - ], + apiArguments: { + body: { cluster: ['manage_security', 'manage_api_key', 'manage_own_api_key'] }, + }, statusCode: 200, result: { areApiKeysEnabled: false, isAdmin: true, canManage: true }, }, @@ -212,52 +129,34 @@ describe('Check API keys privileges', () => { ); getPrivilegesTest('returns isAdmin=false when user has insufficient privileges', { - callAsCurrentUserResponses: [ - async () => ({ - username: 'elastic', - has_all_requested: true, - cluster: { manage_api_key: false, manage_security: false, manage_own_api_key: false }, - index: {}, - application: {}, - }), - ], - callAsInternalUserResponses: [async () => ({})], + apiResponse: async () => ({ + username: 'elastic', + has_all_requested: true, + cluster: { manage_api_key: false, manage_security: false, manage_own_api_key: false }, + index: {}, + application: {}, + }), asserts: { - callAsCurrentUserAPIArguments: [ - [ - 'shield.hasPrivileges', - { body: { cluster: ['manage_security', 'manage_api_key', 'manage_own_api_key'] } }, - ], - ], - callAsInternalUserAPIArguments: [ - ['shield.invalidateAPIKey', { body: { id: expect.any(String) } }], - ], + apiArguments: { + body: { cluster: ['manage_security', 'manage_api_key', 'manage_own_api_key'] }, + }, statusCode: 200, result: { areApiKeysEnabled: true, isAdmin: false, canManage: false }, }, }); getPrivilegesTest('returns canManage=true when user can manage their own API Keys', { - callAsCurrentUserResponses: [ - async () => ({ - username: 'elastic', - has_all_requested: true, - cluster: { manage_api_key: false, manage_security: false, manage_own_api_key: true }, - index: {}, - application: {}, - }), - ], - callAsInternalUserResponses: [async () => ({})], + apiResponse: async () => ({ + username: 'elastic', + has_all_requested: true, + cluster: { manage_api_key: false, manage_security: false, manage_own_api_key: true }, + index: {}, + application: {}, + }), asserts: { - callAsCurrentUserAPIArguments: [ - [ - 'shield.hasPrivileges', - { body: { cluster: ['manage_security', 'manage_api_key', 'manage_own_api_key'] } }, - ], - ], - callAsInternalUserAPIArguments: [ - ['shield.invalidateAPIKey', { body: { id: expect.any(String) } }], - ], + apiArguments: { + body: { cluster: ['manage_security', 'manage_api_key', 'manage_own_api_key'] }, + }, statusCode: 200, result: { areApiKeysEnabled: true, isAdmin: false, canManage: true }, }, diff --git a/x-pack/plugins/security/server/routes/api_keys/privileges.ts b/x-pack/plugins/security/server/routes/api_keys/privileges.ts index 9cccb96752772..dd5d81060c7e5 100644 --- a/x-pack/plugins/security/server/routes/api_keys/privileges.ts +++ b/x-pack/plugins/security/server/routes/api_keys/privileges.ts @@ -8,11 +8,7 @@ import { wrapIntoCustomErrorResponse } from '../../errors'; import { createLicensedRouteHandler } from '../licensed_route_handler'; import { RouteDefinitionParams } from '..'; -export function defineCheckPrivilegesRoutes({ - router, - clusterClient, - authc, -}: RouteDefinitionParams) { +export function defineCheckPrivilegesRoutes({ router, authc }: RouteDefinitionParams) { router.get( { path: '/internal/security/api_key/privileges', @@ -20,19 +16,25 @@ export function defineCheckPrivilegesRoutes({ }, createLicensedRouteHandler(async (context, request, response) => { try { - const scopedClusterClient = clusterClient.asScoped(request); - const [ { - cluster: { - manage_security: manageSecurity, - manage_api_key: manageApiKey, - manage_own_api_key: manageOwnApiKey, + body: { + cluster: { + manage_security: manageSecurity, + manage_api_key: manageApiKey, + manage_own_api_key: manageOwnApiKey, + }, }, }, areApiKeysEnabled, ] = await Promise.all([ - scopedClusterClient.callAsCurrentUser('shield.hasPrivileges', { + context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges<{ + cluster: { + manage_security: boolean; + manage_api_key: boolean; + manage_own_api_key: boolean; + }; + }>({ body: { cluster: ['manage_security', 'manage_api_key', 'manage_own_api_key'] }, }), authc.areAPIKeysEnabled(), diff --git a/x-pack/plugins/security/server/routes/authorization/privileges/get_builtin.ts b/x-pack/plugins/security/server/routes/authorization/privileges/get_builtin.ts index 08cd3ba487b0b..39e6a4838d34d 100644 --- a/x-pack/plugins/security/server/routes/authorization/privileges/get_builtin.ts +++ b/x-pack/plugins/security/server/routes/authorization/privileges/get_builtin.ts @@ -7,13 +7,13 @@ import { BuiltinESPrivileges } from '../../../../common/model'; import { RouteDefinitionParams } from '../..'; -export function defineGetBuiltinPrivilegesRoutes({ router, clusterClient }: RouteDefinitionParams) { +export function defineGetBuiltinPrivilegesRoutes({ router }: RouteDefinitionParams) { router.get( { path: '/internal/security/esPrivileges/builtin', validate: false }, async (context, request, response) => { - const privileges: BuiltinESPrivileges = await clusterClient - .asScoped(request) - .callAsCurrentUser('shield.getBuiltinPrivileges'); + const { + body: privileges, + } = await context.core.elasticsearch.client.asCurrentUser.security.getBuiltinPrivileges(); // Exclude the `none` privilege, as it doesn't make sense as an option within the Kibana UI privileges.cluster = privileges.cluster.filter((privilege) => privilege !== 'none'); diff --git a/x-pack/plugins/security/server/routes/authorization/roles/delete.test.ts b/x-pack/plugins/security/server/routes/authorization/roles/delete.test.ts index 9f5ec635f56cd..5143b727fcb4e 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/delete.test.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/delete.test.ts @@ -5,14 +5,11 @@ */ import Boom from '@hapi/boom'; -import { kibanaResponseFactory, RequestHandlerContext } from '../../../../../../../src/core/server'; +import { kibanaResponseFactory } from '../../../../../../../src/core/server'; import { LicenseCheck } from '../../../../../licensing/server'; import { defineDeleteRolesRoutes } from './delete'; -import { - elasticsearchServiceMock, - httpServerMock, -} from '../../../../../../../src/core/server/mocks'; +import { coreMock, httpServerMock } from '../../../../../../../src/core/server/mocks'; import { routeDefinitionParamsMock } from '../../index.mock'; interface TestOptions { @@ -29,11 +26,15 @@ describe('DELETE role', () => { ) => { test(description, async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); + const mockContext = { + core: coreMock.createRequestHandlerContext(), + licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } } as any, + }; - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockRouteDefinitionParams.clusterClient.asScoped.mockReturnValue(mockScopedClusterClient); if (apiResponse) { - mockScopedClusterClient.callAsCurrentUser.mockImplementation(apiResponse); + mockContext.core.elasticsearch.client.asCurrentUser.security.deleteRole.mockImplementation( + (async () => ({ body: await apiResponse() })) as any + ); } defineDeleteRolesRoutes(mockRouteDefinitionParams); @@ -46,22 +47,15 @@ describe('DELETE role', () => { params: { name }, headers, }); - const mockContext = ({ - licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } }, - } as unknown) as RequestHandlerContext; const response = await handler(mockContext, mockRequest, kibanaResponseFactory); expect(response.status).toBe(asserts.statusCode); expect(response.payload).toEqual(asserts.result); if (apiResponse) { - expect(mockRouteDefinitionParams.clusterClient.asScoped).toHaveBeenCalledWith(mockRequest); - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith( - 'shield.deleteRole', - { name } - ); - } else { - expect(mockScopedClusterClient.callAsCurrentUser).not.toHaveBeenCalled(); + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.deleteRole + ).toHaveBeenCalledWith({ name }); } expect(mockContext.licensing.license.check).toHaveBeenCalledWith('security', 'basic'); }); diff --git a/x-pack/plugins/security/server/routes/authorization/roles/delete.ts b/x-pack/plugins/security/server/routes/authorization/roles/delete.ts index eb56143288747..b877aaf6abd77 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/delete.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/delete.ts @@ -9,7 +9,7 @@ import { RouteDefinitionParams } from '../../index'; import { createLicensedRouteHandler } from '../../licensed_route_handler'; import { wrapIntoCustomErrorResponse } from '../../../errors'; -export function defineDeleteRolesRoutes({ router, clusterClient }: RouteDefinitionParams) { +export function defineDeleteRolesRoutes({ router }: RouteDefinitionParams) { router.delete( { path: '/api/security/role/{name}', @@ -19,7 +19,7 @@ export function defineDeleteRolesRoutes({ router, clusterClient }: RouteDefiniti }, createLicensedRouteHandler(async (context, request, response) => { try { - await clusterClient.asScoped(request).callAsCurrentUser('shield.deleteRole', { + await context.core.elasticsearch.client.asCurrentUser.security.deleteRole({ name: request.params.name, }); diff --git a/x-pack/plugins/security/server/routes/authorization/roles/get.test.ts b/x-pack/plugins/security/server/routes/authorization/roles/get.test.ts index b25b13b9fc04a..a6090ee405329 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/get.test.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/get.test.ts @@ -4,14 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ import Boom from '@hapi/boom'; -import { kibanaResponseFactory, RequestHandlerContext } from '../../../../../../../src/core/server'; +import { kibanaResponseFactory } from '../../../../../../../src/core/server'; import { LicenseCheck } from '../../../../../licensing/server'; import { defineGetRolesRoutes } from './get'; -import { - elasticsearchServiceMock, - httpServerMock, -} from '../../../../../../../src/core/server/mocks'; +import { coreMock, httpServerMock } from '../../../../../../../src/core/server/mocks'; import { routeDefinitionParamsMock } from '../../index.mock'; const application = 'kibana-.kibana'; @@ -32,11 +29,15 @@ describe('GET role', () => { test(description, async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); mockRouteDefinitionParams.authz.applicationName = application; + const mockContext = { + core: coreMock.createRequestHandlerContext(), + licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } } as any, + }; - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockRouteDefinitionParams.clusterClient.asScoped.mockReturnValue(mockScopedClusterClient); if (apiResponse) { - mockScopedClusterClient.callAsCurrentUser.mockImplementation(apiResponse); + mockContext.core.elasticsearch.client.asCurrentUser.security.getRole.mockImplementation( + (async () => ({ body: await apiResponse() })) as any + ); } defineGetRolesRoutes(mockRouteDefinitionParams); @@ -49,22 +50,17 @@ describe('GET role', () => { params: { name }, headers, }); - const mockContext = ({ - licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } }, - } as unknown) as RequestHandlerContext; const response = await handler(mockContext, mockRequest, kibanaResponseFactory); expect(response.status).toBe(asserts.statusCode); expect(response.payload).toEqual(asserts.result); if (apiResponse) { - expect(mockRouteDefinitionParams.clusterClient.asScoped).toHaveBeenCalledWith(mockRequest); - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith('shield.getRole', { - name, - }); - } else { - expect(mockScopedClusterClient.callAsCurrentUser).not.toHaveBeenCalled(); + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.getRole + ).toHaveBeenCalledWith({ name }); } + expect(mockContext.licensing.license.check).toHaveBeenCalledWith('security', 'basic'); }); }; diff --git a/x-pack/plugins/security/server/routes/authorization/roles/get.ts b/x-pack/plugins/security/server/routes/authorization/roles/get.ts index bf1140e2e6fd1..ce4a622d30e61 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/get.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/get.ts @@ -8,9 +8,9 @@ import { schema } from '@kbn/config-schema'; import { RouteDefinitionParams } from '../..'; import { createLicensedRouteHandler } from '../../licensed_route_handler'; import { wrapIntoCustomErrorResponse } from '../../../errors'; -import { transformElasticsearchRoleToRole } from './model'; +import { ElasticsearchRole, transformElasticsearchRoleToRole } from './model'; -export function defineGetRolesRoutes({ router, authz, clusterClient }: RouteDefinitionParams) { +export function defineGetRolesRoutes({ router, authz }: RouteDefinitionParams) { router.get( { path: '/api/security/role/{name}', @@ -20,9 +20,11 @@ export function defineGetRolesRoutes({ router, authz, clusterClient }: RouteDefi }, createLicensedRouteHandler(async (context, request, response) => { try { - const elasticsearchRoles = await clusterClient - .asScoped(request) - .callAsCurrentUser('shield.getRole', { name: request.params.name }); + const { + body: elasticsearchRoles, + } = await context.core.elasticsearch.client.asCurrentUser.security.getRole< + Record + >({ name: request.params.name }); const elasticsearchRole = elasticsearchRoles[request.params.name]; if (elasticsearchRole) { diff --git a/x-pack/plugins/security/server/routes/authorization/roles/get_all.test.ts b/x-pack/plugins/security/server/routes/authorization/roles/get_all.test.ts index 30e0c52c4c443..b3a855b2e0ae7 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/get_all.test.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/get_all.test.ts @@ -4,14 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ import Boom from '@hapi/boom'; -import { kibanaResponseFactory, RequestHandlerContext } from '../../../../../../../src/core/server'; +import { kibanaResponseFactory } from '../../../../../../../src/core/server'; import { LicenseCheck } from '../../../../../licensing/server'; import { defineGetAllRolesRoutes } from './get_all'; -import { - elasticsearchServiceMock, - httpServerMock, -} from '../../../../../../../src/core/server/mocks'; +import { coreMock, httpServerMock } from '../../../../../../../src/core/server/mocks'; import { routeDefinitionParamsMock } from '../../index.mock'; const application = 'kibana-.kibana'; @@ -32,11 +29,15 @@ describe('GET all roles', () => { test(description, async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); mockRouteDefinitionParams.authz.applicationName = application; + const mockContext = { + core: coreMock.createRequestHandlerContext(), + licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } } as any, + }; - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockRouteDefinitionParams.clusterClient.asScoped.mockReturnValue(mockScopedClusterClient); if (apiResponse) { - mockScopedClusterClient.callAsCurrentUser.mockImplementation(apiResponse); + mockContext.core.elasticsearch.client.asCurrentUser.security.getRole.mockImplementation( + (async () => ({ body: await apiResponse() })) as any + ); } defineGetAllRolesRoutes(mockRouteDefinitionParams); @@ -48,19 +49,15 @@ describe('GET all roles', () => { path: '/api/security/role', headers, }); - const mockContext = ({ - licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } }, - } as unknown) as RequestHandlerContext; const response = await handler(mockContext, mockRequest, kibanaResponseFactory); expect(response.status).toBe(asserts.statusCode); expect(response.payload).toEqual(asserts.result); if (apiResponse) { - expect(mockRouteDefinitionParams.clusterClient.asScoped).toHaveBeenCalledWith(mockRequest); - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith('shield.getRole'); - } else { - expect(mockScopedClusterClient.callAsCurrentUser).not.toHaveBeenCalled(); + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.getRole + ).toHaveBeenCalled(); } expect(mockContext.licensing.license.check).toHaveBeenCalledWith('security', 'basic'); }); diff --git a/x-pack/plugins/security/server/routes/authorization/roles/get_all.ts b/x-pack/plugins/security/server/routes/authorization/roles/get_all.ts index 24be6c60e4b12..21521dd6dbae3 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/get_all.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/get_all.ts @@ -9,14 +9,16 @@ import { createLicensedRouteHandler } from '../../licensed_route_handler'; import { wrapIntoCustomErrorResponse } from '../../../errors'; import { ElasticsearchRole, transformElasticsearchRoleToRole } from './model'; -export function defineGetAllRolesRoutes({ router, authz, clusterClient }: RouteDefinitionParams) { +export function defineGetAllRolesRoutes({ router, authz }: RouteDefinitionParams) { router.get( { path: '/api/security/role', validate: false }, createLicensedRouteHandler(async (context, request, response) => { try { - const elasticsearchRoles = (await clusterClient - .asScoped(request) - .callAsCurrentUser('shield.getRole')) as Record; + const { + body: elasticsearchRoles, + } = await context.core.elasticsearch.client.asCurrentUser.security.getRole< + Record + >(); // Transform elasticsearch roles into Kibana roles and return in a list sorted by the role name. return response.ok({ diff --git a/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts b/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts index 811ea080b4316..779e1a7fab177 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts @@ -5,15 +5,12 @@ */ import { Type } from '@kbn/config-schema'; -import { kibanaResponseFactory, RequestHandlerContext } from '../../../../../../../src/core/server'; +import { kibanaResponseFactory } from '../../../../../../../src/core/server'; import { LicenseCheck } from '../../../../../licensing/server'; import { GLOBAL_RESOURCE } from '../../../../common/constants'; import { definePutRolesRoutes } from './put'; -import { - elasticsearchServiceMock, - httpServerMock, -} from '../../../../../../../src/core/server/mocks'; +import { coreMock, httpServerMock } from '../../../../../../../src/core/server/mocks'; import { routeDefinitionParamsMock } from '../../index.mock'; import { KibanaFeature } from '../../../../../features/server'; import { securityFeatureUsageServiceMock } from '../../../feature_usage/index.mock'; @@ -47,35 +44,43 @@ const privilegeMap = { interface TestOptions { name: string; licenseCheckResult?: LicenseCheck; - apiResponses?: Array<() => Promise>; + apiResponses?: { + get: () => Promise; + put: () => Promise; + }; payload?: Record; asserts: { statusCode: number; result?: Record; - apiArguments?: unknown[][]; + apiArguments?: { get: unknown[]; put: unknown[] }; recordSubFeaturePrivilegeUsage?: boolean; }; } const putRoleTest = ( description: string, - { - name, - payload, - licenseCheckResult = { state: 'valid' }, - apiResponses = [], - asserts, - }: TestOptions + { name, payload, licenseCheckResult = { state: 'valid' }, apiResponses, asserts }: TestOptions ) => { test(description, async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); mockRouteDefinitionParams.authz.applicationName = application; mockRouteDefinitionParams.authz.privileges.get.mockReturnValue(privilegeMap); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockRouteDefinitionParams.clusterClient.asScoped.mockReturnValue(mockScopedClusterClient); - for (const apiResponse of apiResponses) { - mockScopedClusterClient.callAsCurrentUser.mockImplementationOnce(apiResponse); + const mockContext = { + core: coreMock.createRequestHandlerContext(), + licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } } as any, + }; + + if (apiResponses?.get) { + mockContext.core.elasticsearch.client.asCurrentUser.security.getRole.mockImplementationOnce( + (async () => ({ body: await apiResponses?.get() })) as any + ); + } + + if (apiResponses?.put) { + mockContext.core.elasticsearch.client.asCurrentUser.security.putRole.mockImplementationOnce( + (async () => ({ body: await apiResponses?.put() })) as any + ); } mockRouteDefinitionParams.getFeatureUsageService.mockReturnValue( @@ -131,21 +136,20 @@ const putRoleTest = ( body: payload !== undefined ? (validate as any).body.validate(payload) : undefined, headers, }); - const mockContext = ({ - licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } }, - } as unknown) as RequestHandlerContext; const response = await handler(mockContext, mockRequest, kibanaResponseFactory); expect(response.status).toBe(asserts.statusCode); expect(response.payload).toEqual(asserts.result); - if (Array.isArray(asserts.apiArguments)) { - for (const apiArguments of asserts.apiArguments) { - expect(mockRouteDefinitionParams.clusterClient.asScoped).toHaveBeenCalledWith(mockRequest); - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith(...apiArguments); - } - } else { - expect(mockScopedClusterClient.callAsCurrentUser).not.toHaveBeenCalled(); + if (asserts.apiArguments?.get) { + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.getRole + ).toHaveBeenCalledWith(...asserts.apiArguments?.get); + } + if (asserts.apiArguments?.put) { + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.putRole + ).toHaveBeenCalledWith(...asserts.apiArguments?.put); } expect(mockContext.licensing.license.check).toHaveBeenCalledWith('security', 'basic'); @@ -208,12 +212,11 @@ describe('PUT role', () => { putRoleTest(`creates empty role`, { name: 'foo-role', payload: {}, - apiResponses: [async () => ({}), async () => {}], + apiResponses: { get: async () => ({}), put: async () => {} }, asserts: { - apiArguments: [ - ['shield.getRole', { name: 'foo-role', ignore: [404] }], - [ - 'shield.putRole', + apiArguments: { + get: [{ name: 'foo-role' }, { ignore: [404] }], + put: [ { name: 'foo-role', body: { @@ -224,7 +227,7 @@ describe('PUT role', () => { }, }, ], - ], + }, statusCode: 204, result: undefined, }, @@ -239,12 +242,11 @@ describe('PUT role', () => { }, ], }, - apiResponses: [async () => ({}), async () => {}], + apiResponses: { get: async () => ({}), put: async () => {} }, asserts: { - apiArguments: [ - ['shield.getRole', { name: 'foo-role', ignore: [404] }], - [ - 'shield.putRole', + apiArguments: { + get: [{ name: 'foo-role' }, { ignore: [404] }], + put: [ { name: 'foo-role', body: { @@ -261,7 +263,7 @@ describe('PUT role', () => { }, }, ], - ], + }, statusCode: 204, result: undefined, }, @@ -279,12 +281,11 @@ describe('PUT role', () => { }, ], }, - apiResponses: [async () => ({}), async () => {}], + apiResponses: { get: async () => ({}), put: async () => {} }, asserts: { - apiArguments: [ - ['shield.getRole', { name: 'foo-role', ignore: [404] }], - [ - 'shield.putRole', + apiArguments: { + get: [{ name: 'foo-role' }, { ignore: [404] }], + put: [ { name: 'foo-role', body: { @@ -301,7 +302,7 @@ describe('PUT role', () => { }, }, ], - ], + }, statusCode: 204, result: undefined, }, @@ -317,12 +318,11 @@ describe('PUT role', () => { }, ], }, - apiResponses: [async () => ({}), async () => {}], + apiResponses: { get: async () => ({}), put: async () => {} }, asserts: { - apiArguments: [ - ['shield.getRole', { name: 'foo-role', ignore: [404] }], - [ - 'shield.putRole', + apiArguments: { + get: [{ name: 'foo-role' }, { ignore: [404] }], + put: [ { name: 'foo-role', body: { @@ -339,7 +339,7 @@ describe('PUT role', () => { }, }, ], - ], + }, statusCode: 204, result: undefined, }, @@ -383,12 +383,11 @@ describe('PUT role', () => { }, ], }, - apiResponses: [async () => ({}), async () => {}], + apiResponses: { get: async () => ({}), put: async () => {} }, asserts: { - apiArguments: [ - ['shield.getRole', { name: 'foo-role', ignore: [404] }], - [ - 'shield.putRole', + apiArguments: { + get: [{ name: 'foo-role' }, { ignore: [404] }], + put: [ { name: 'foo-role', body: { @@ -426,7 +425,7 @@ describe('PUT role', () => { }, }, ], - ], + }, statusCode: 204, result: undefined, }, @@ -473,8 +472,8 @@ describe('PUT role', () => { }, ], }, - apiResponses: [ - async () => ({ + apiResponses: { + get: async () => ({ 'foo-role': { metadata: { bar: 'old-metadata', @@ -504,13 +503,12 @@ describe('PUT role', () => { ], }, }), - async () => {}, - ], + put: async () => {}, + }, asserts: { - apiArguments: [ - ['shield.getRole', { name: 'foo-role', ignore: [404] }], - [ - 'shield.putRole', + apiArguments: { + get: [{ name: 'foo-role' }, { ignore: [404] }], + put: [ { name: 'foo-role', body: { @@ -548,7 +546,7 @@ describe('PUT role', () => { }, }, ], - ], + }, statusCode: 204, result: undefined, }, @@ -577,8 +575,8 @@ describe('PUT role', () => { }, ], }, - apiResponses: [ - async () => ({ + apiResponses: { + get: async () => ({ 'foo-role': { metadata: { bar: 'old-metadata', @@ -613,13 +611,12 @@ describe('PUT role', () => { ], }, }), - async () => {}, - ], + put: async () => {}, + }, asserts: { - apiArguments: [ - ['shield.getRole', { name: 'foo-role', ignore: [404] }], - [ - 'shield.putRole', + apiArguments: { + get: [{ name: 'foo-role' }, { ignore: [404] }], + put: [ { name: 'foo-role', body: { @@ -652,7 +649,7 @@ describe('PUT role', () => { }, }, ], - ], + }, statusCode: 204, result: undefined, }, @@ -670,13 +667,12 @@ describe('PUT role', () => { }, ], }, - apiResponses: [async () => ({}), async () => {}], + apiResponses: { get: async () => ({}), put: async () => {} }, asserts: { recordSubFeaturePrivilegeUsage: true, - apiArguments: [ - ['shield.getRole', { name: 'foo-role', ignore: [404] }], - [ - 'shield.putRole', + apiArguments: { + get: [{ name: 'foo-role' }, { ignore: [404] }], + put: [ { name: 'foo-role', body: { @@ -694,7 +690,7 @@ describe('PUT role', () => { }, }, ], - ], + }, statusCode: 204, result: undefined, }, @@ -712,13 +708,12 @@ describe('PUT role', () => { }, ], }, - apiResponses: [async () => ({}), async () => {}], + apiResponses: { get: async () => ({}), put: async () => {} }, asserts: { recordSubFeaturePrivilegeUsage: false, - apiArguments: [ - ['shield.getRole', { name: 'foo-role', ignore: [404] }], - [ - 'shield.putRole', + apiArguments: { + get: [{ name: 'foo-role' }, { ignore: [404] }], + put: [ { name: 'foo-role', body: { @@ -736,7 +731,7 @@ describe('PUT role', () => { }, }, ], - ], + }, statusCode: 204, result: undefined, }, @@ -754,13 +749,12 @@ describe('PUT role', () => { }, ], }, - apiResponses: [async () => ({}), async () => {}], + apiResponses: { get: async () => ({}), put: async () => {} }, asserts: { recordSubFeaturePrivilegeUsage: false, - apiArguments: [ - ['shield.getRole', { name: 'foo-role', ignore: [404] }], - [ - 'shield.putRole', + apiArguments: { + get: [{ name: 'foo-role' }, { ignore: [404] }], + put: [ { name: 'foo-role', body: { @@ -778,7 +772,7 @@ describe('PUT role', () => { }, }, ], - ], + }, statusCode: 204, result: undefined, }, diff --git a/x-pack/plugins/security/server/routes/authorization/roles/put.ts b/x-pack/plugins/security/server/routes/authorization/roles/put.ts index cdedc9ac8a5eb..26c61b4ced15f 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/put.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/put.ts @@ -42,7 +42,6 @@ const roleGrantsSubFeaturePrivileges = ( export function definePutRolesRoutes({ router, authz, - clusterClient, getFeatures, getFeatureUsageService, }: RouteDefinitionParams) { @@ -64,12 +63,11 @@ export function definePutRolesRoutes({ const { name } = request.params; try { - const rawRoles: Record = await clusterClient - .asScoped(request) - .callAsCurrentUser('shield.getRole', { - name: request.params.name, - ignore: [404], - }); + const { + body: rawRoles, + } = await context.core.elasticsearch.client.asCurrentUser.security.getRole< + Record + >({ name: request.params.name }, { ignore: [404] }); const body = transformPutPayloadToElasticsearchRole( request.body, @@ -77,11 +75,12 @@ export function definePutRolesRoutes({ rawRoles[name] ? rawRoles[name].applications : [] ); - const [features] = await Promise.all([ + const [features] = await Promise.all([ getFeatures(), - clusterClient - .asScoped(request) - .callAsCurrentUser('shield.putRole', { name: request.params.name, body }), + context.core.elasticsearch.client.asCurrentUser.security.putRole({ + name: request.params.name, + body, + }), ]); if (roleGrantsSubFeaturePrivileges(features, request.body)) { diff --git a/x-pack/plugins/security/server/routes/index.mock.ts b/x-pack/plugins/security/server/routes/index.mock.ts index fab4a71df0cb0..1df499d981632 100644 --- a/x-pack/plugins/security/server/routes/index.mock.ts +++ b/x-pack/plugins/security/server/routes/index.mock.ts @@ -5,7 +5,6 @@ */ import { - elasticsearchServiceMock, httpServiceMock, loggingSystemMock, httpResourcesMock, @@ -25,7 +24,6 @@ export const routeDefinitionParamsMock = { basePath: httpServiceMock.createBasePath(), csp: httpServiceMock.createSetupContract().csp, logger: loggingSystemMock.create().get(), - clusterClient: elasticsearchServiceMock.createLegacyClusterClient(), config: createConfig(ConfigSchema.validate(config), loggingSystemMock.create().get(), { isTLSEnabled: false, }), diff --git a/x-pack/plugins/security/server/routes/index.ts b/x-pack/plugins/security/server/routes/index.ts index 079c9e8ab9ce7..db71b04b3e6f0 100644 --- a/x-pack/plugins/security/server/routes/index.ts +++ b/x-pack/plugins/security/server/routes/index.ts @@ -5,13 +5,7 @@ */ import type { PublicMethodsOf } from '@kbn/utility-types'; import { KibanaFeature } from '../../../features/server'; -import { - HttpResources, - IBasePath, - ILegacyClusterClient, - IRouter, - Logger, -} from '../../../../../src/core/server'; +import { HttpResources, IBasePath, IRouter, Logger } from '../../../../../src/core/server'; import { SecurityLicense } from '../../common/licensing'; import { Authentication } from '../authentication'; import { AuthorizationServiceSetup } from '../authorization'; @@ -36,7 +30,6 @@ export interface RouteDefinitionParams { basePath: IBasePath; httpResources: HttpResources; logger: Logger; - clusterClient: ILegacyClusterClient; config: ConfigType; authc: Authentication; authz: AuthorizationServiceSetup; diff --git a/x-pack/plugins/security/server/routes/indices/get_fields.test.ts b/x-pack/plugins/security/server/routes/indices/get_fields.test.ts index 4c6182e99431d..6d3de11249a16 100644 --- a/x-pack/plugins/security/server/routes/indices/get_fields.test.ts +++ b/x-pack/plugins/security/server/routes/indices/get_fields.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { httpServerMock, elasticsearchServiceMock } from '../../../../../../src/core/server/mocks'; +import { httpServerMock, coreMock } from '../../../../../../src/core/server/mocks'; import { kibanaResponseFactory } from '../../../../../../src/core/server'; import { routeDefinitionParamsMock } from '../index.mock'; @@ -36,10 +36,12 @@ const mockFieldMappingResponse = { describe('GET /internal/security/fields/{query}', () => { it('returns a list of deduplicated fields, omitting empty and runtime fields', async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); - - const scopedClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - scopedClient.callAsCurrentUser.mockResolvedValue(mockFieldMappingResponse); - mockRouteDefinitionParams.clusterClient.asScoped.mockReturnValue(scopedClient); + const mockContext = { + core: coreMock.createRequestHandlerContext(), + }; + mockContext.core.elasticsearch.client.asCurrentUser.indices.getFieldMapping.mockImplementation( + (async () => ({ body: mockFieldMappingResponse })) as any + ); defineGetFieldsRoutes(mockRouteDefinitionParams); @@ -51,7 +53,7 @@ describe('GET /internal/security/fields/{query}', () => { path: `/internal/security/fields/foo`, headers, }); - const response = await handler({} as any, mockRequest, kibanaResponseFactory); + const response = await handler(mockContext as any, mockRequest, kibanaResponseFactory); expect(response.status).toBe(200); expect(response.payload).toEqual(['fooField', 'commonField', 'barField']); }); diff --git a/x-pack/plugins/security/server/routes/indices/get_fields.ts b/x-pack/plugins/security/server/routes/indices/get_fields.ts index 44b8804ed8d6e..304e121f7fee1 100644 --- a/x-pack/plugins/security/server/routes/indices/get_fields.ts +++ b/x-pack/plugins/security/server/routes/indices/get_fields.ts @@ -22,7 +22,7 @@ interface FieldMappingResponse { }; } -export function defineGetFieldsRoutes({ router, clusterClient }: RouteDefinitionParams) { +export function defineGetFieldsRoutes({ router }: RouteDefinitionParams) { router.get( { path: '/internal/security/fields/{query}', @@ -30,14 +30,16 @@ export function defineGetFieldsRoutes({ router, clusterClient }: RouteDefinition }, async (context, request, response) => { try { - const indexMappings = (await clusterClient - .asScoped(request) - .callAsCurrentUser('indices.getFieldMapping', { + const { + body: indexMappings, + } = await context.core.elasticsearch.client.asCurrentUser.indices.getFieldMapping( + { index: request.params.query, fields: '*', - allowNoIndices: false, - includeDefaults: true, - })) as FieldMappingResponse; + allow_no_indices: false, + include_defaults: true, + } + ); // The flow is the following (see response format at https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-get-field-mapping.html): // 1. Iterate over all matched indices. diff --git a/x-pack/plugins/security/server/routes/role_mapping/delete.test.ts b/x-pack/plugins/security/server/routes/role_mapping/delete.test.ts index aec0310129f6e..33fd66f9e929d 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/delete.test.ts +++ b/x-pack/plugins/security/server/routes/role_mapping/delete.test.ts @@ -5,24 +5,26 @@ */ import { routeDefinitionParamsMock } from '../index.mock'; -import { elasticsearchServiceMock, httpServerMock } from 'src/core/server/mocks'; -import { kibanaResponseFactory, RequestHandlerContext } from '../../../../../../src/core/server'; +import { coreMock, httpServerMock } from 'src/core/server/mocks'; +import { kibanaResponseFactory } from '../../../../../../src/core/server'; import { defineRoleMappingDeleteRoutes } from './delete'; describe('DELETE role mappings', () => { it('allows a role mapping to be deleted', async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); - - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockRouteDefinitionParams.clusterClient.asScoped.mockReturnValue(mockScopedClusterClient); - mockScopedClusterClient.callAsCurrentUser.mockResolvedValue({ acknowledged: true }); + const mockContext = { + core: coreMock.createRequestHandlerContext(), + licensing: { license: { check: jest.fn().mockReturnValue({ state: 'valid' }) } } as any, + }; + mockContext.core.elasticsearch.client.asCurrentUser.security.deleteRoleMapping.mockResolvedValue( + { body: { acknowledged: true } } as any + ); defineRoleMappingDeleteRoutes(mockRouteDefinitionParams); const [[, handler]] = mockRouteDefinitionParams.router.delete.mock.calls; const name = 'mapping1'; - const headers = { authorization: 'foo' }; const mockRequest = httpServerMock.createKibanaRequest({ method: 'delete', @@ -30,31 +32,35 @@ describe('DELETE role mappings', () => { params: { name }, headers, }); - const mockContext = ({ - licensing: { - license: { check: jest.fn().mockReturnValue({ state: 'valid' }) }, - }, - } as unknown) as RequestHandlerContext; const response = await handler(mockContext, mockRequest, kibanaResponseFactory); expect(response.status).toBe(200); expect(response.payload).toEqual({ acknowledged: true }); - expect(mockRouteDefinitionParams.clusterClient.asScoped).toHaveBeenCalledWith(mockRequest); expect( - mockScopedClusterClient.callAsCurrentUser - ).toHaveBeenCalledWith('shield.deleteRoleMapping', { name }); + mockContext.core.elasticsearch.client.asCurrentUser.security.deleteRoleMapping + ).toHaveBeenCalledWith({ name }); }); describe('failure', () => { it('returns result of license check', async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); + const mockContext = { + core: coreMock.createRequestHandlerContext(), + licensing: { + license: { + check: jest.fn().mockReturnValue({ + state: 'invalid', + message: 'test forbidden message', + }), + }, + } as any, + }; defineRoleMappingDeleteRoutes(mockRouteDefinitionParams); const [[, handler]] = mockRouteDefinitionParams.router.delete.mock.calls; const name = 'mapping1'; - const headers = { authorization: 'foo' }; const mockRequest = httpServerMock.createKibanaRequest({ method: 'delete', @@ -62,21 +68,13 @@ describe('DELETE role mappings', () => { params: { name }, headers, }); - const mockContext = ({ - licensing: { - license: { - check: jest.fn().mockReturnValue({ - state: 'invalid', - message: 'test forbidden message', - }), - }, - }, - } as unknown) as RequestHandlerContext; const response = await handler(mockContext, mockRequest, kibanaResponseFactory); expect(response.status).toBe(403); expect(response.payload).toEqual({ message: 'test forbidden message' }); - expect(mockRouteDefinitionParams.clusterClient.asScoped).not.toHaveBeenCalled(); + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.deleteRoleMapping + ).not.toHaveBeenCalled(); }); }); }); diff --git a/x-pack/plugins/security/server/routes/role_mapping/delete.ts b/x-pack/plugins/security/server/routes/role_mapping/delete.ts index dc11bcd914b35..dbe9c5662a1f1 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/delete.ts +++ b/x-pack/plugins/security/server/routes/role_mapping/delete.ts @@ -8,9 +8,7 @@ import { createLicensedRouteHandler } from '../licensed_route_handler'; import { wrapError } from '../../errors'; import { RouteDefinitionParams } from '..'; -export function defineRoleMappingDeleteRoutes(params: RouteDefinitionParams) { - const { clusterClient, router } = params; - +export function defineRoleMappingDeleteRoutes({ router }: RouteDefinitionParams) { router.delete( { path: '/internal/security/role_mapping/{name}', @@ -22,11 +20,11 @@ export function defineRoleMappingDeleteRoutes(params: RouteDefinitionParams) { }, createLicensedRouteHandler(async (context, request, response) => { try { - const deleteResponse = await clusterClient - .asScoped(request) - .callAsCurrentUser('shield.deleteRoleMapping', { - name: request.params.name, - }); + const { + body: deleteResponse, + } = await context.core.elasticsearch.client.asCurrentUser.security.deleteRoleMapping({ + name: request.params.name, + }); return response.ok({ body: deleteResponse }); } catch (error) { const wrappedError = wrapError(error); diff --git a/x-pack/plugins/security/server/routes/role_mapping/feature_check.test.ts b/x-pack/plugins/security/server/routes/role_mapping/feature_check.test.ts index ee1d550bbe24d..8bd9f095b0f68 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/feature_check.test.ts +++ b/x-pack/plugins/security/server/routes/role_mapping/feature_check.test.ts @@ -5,21 +5,16 @@ */ import { routeDefinitionParamsMock } from '../index.mock'; -import { elasticsearchServiceMock, httpServerMock } from 'src/core/server/mocks'; -import { - kibanaResponseFactory, - RequestHandlerContext, - ILegacyClusterClient, -} from '../../../../../../src/core/server'; +import { coreMock, httpServerMock } from 'src/core/server/mocks'; +import { kibanaResponseFactory } from '../../../../../../src/core/server'; import { LicenseCheck } from '../../../../licensing/server'; import { defineRoleMappingFeatureCheckRoute } from './feature_check'; interface TestOptions { licenseCheckResult?: LicenseCheck; canManageRoleMappings?: boolean; - nodeSettingsResponse?: Record; - xpackUsageResponse?: Record; - internalUserClusterClientImpl?: ILegacyClusterClient['callAsInternalUser']; + nodeSettingsResponse?: () => Record; + xpackUsageResponse?: () => Record; asserts: { statusCode: number; result?: Record }; } @@ -38,57 +33,34 @@ const defaultXpackUsageResponse = { }, }; -const getDefaultInternalUserClusterClientImpl = ( - nodeSettingsResponse: TestOptions['nodeSettingsResponse'], - xpackUsageResponse: TestOptions['xpackUsageResponse'] -) => - ((async (endpoint: string, clientParams: Record) => { - if (!clientParams) throw new TypeError('expected clientParams'); - - if (endpoint === 'nodes.info') { - return nodeSettingsResponse; - } - - if (endpoint === 'transport.request') { - if (clientParams.path === '/_xpack/usage') { - return xpackUsageResponse; - } - } - - throw new Error(`unexpected endpoint: ${endpoint}`); - }) as unknown) as TestOptions['internalUserClusterClientImpl']; - describe('GET role mappings feature check', () => { const getFeatureCheckTest = ( description: string, { licenseCheckResult = { state: 'valid' }, canManageRoleMappings = true, - nodeSettingsResponse = {}, - xpackUsageResponse = defaultXpackUsageResponse, - internalUserClusterClientImpl = getDefaultInternalUserClusterClientImpl( - nodeSettingsResponse, - xpackUsageResponse - ), + nodeSettingsResponse = async () => ({}), + xpackUsageResponse = async () => defaultXpackUsageResponse, asserts, }: TestOptions ) => { test(description, async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); + const mockContext = { + core: coreMock.createRequestHandlerContext(), + licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } } as any, + }; - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockRouteDefinitionParams.clusterClient.asScoped.mockReturnValue(mockScopedClusterClient); - mockRouteDefinitionParams.clusterClient.callAsInternalUser.mockImplementation( - internalUserClusterClientImpl + mockContext.core.elasticsearch.client.asInternalUser.nodes.info.mockImplementation( + (async () => ({ body: await nodeSettingsResponse() })) as any + ); + mockContext.core.elasticsearch.client.asInternalUser.transport.request.mockImplementation( + (async () => ({ body: await xpackUsageResponse() })) as any ); - mockScopedClusterClient.callAsCurrentUser.mockImplementation(async (method, payload) => { - if (method === 'shield.hasPrivileges') { - return { - has_all_requested: canManageRoleMappings, - }; - } - }); + mockContext.core.elasticsearch.client.asCurrentUser.security.hasPrivileges.mockResolvedValue({ + body: { has_all_requested: canManageRoleMappings }, + } as any); defineRoleMappingFeatureCheckRoute(mockRouteDefinitionParams); const [[, handler]] = mockRouteDefinitionParams.router.get.mock.calls; @@ -99,9 +71,6 @@ describe('GET role mappings feature check', () => { path: `/internal/security/_check_role_mapping_features`, headers, }); - const mockContext = ({ - licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } }, - } as unknown) as RequestHandlerContext; const response = await handler(mockContext, mockRequest, kibanaResponseFactory); expect(response.status).toBe(asserts.statusCode); @@ -124,7 +93,7 @@ describe('GET role mappings feature check', () => { }); getFeatureCheckTest('allows both script types when explicitly enabled', { - nodeSettingsResponse: { + nodeSettingsResponse: async () => ({ nodes: { someNodeId: { settings: { @@ -134,7 +103,7 @@ describe('GET role mappings feature check', () => { }, }, }, - }, + }), asserts: { statusCode: 200, result: { @@ -147,7 +116,7 @@ describe('GET role mappings feature check', () => { }); getFeatureCheckTest('disallows stored scripts when disabled', { - nodeSettingsResponse: { + nodeSettingsResponse: async () => ({ nodes: { someNodeId: { settings: { @@ -157,7 +126,7 @@ describe('GET role mappings feature check', () => { }, }, }, - }, + }), asserts: { statusCode: 200, result: { @@ -170,7 +139,7 @@ describe('GET role mappings feature check', () => { }); getFeatureCheckTest('disallows inline scripts when disabled', { - nodeSettingsResponse: { + nodeSettingsResponse: async () => ({ nodes: { someNodeId: { settings: { @@ -180,7 +149,7 @@ describe('GET role mappings feature check', () => { }, }, }, - }, + }), asserts: { statusCode: 200, result: { @@ -193,7 +162,7 @@ describe('GET role mappings feature check', () => { }); getFeatureCheckTest('indicates incompatible realms when only native and file are enabled', { - xpackUsageResponse: { + xpackUsageResponse: async () => ({ security: { realms: { native: { @@ -206,7 +175,7 @@ describe('GET role mappings feature check', () => { }, }, }, - }, + }), asserts: { statusCode: 200, result: { @@ -231,9 +200,12 @@ describe('GET role mappings feature check', () => { getFeatureCheckTest( 'falls back to allowing both script types if there is an error retrieving node settings', { - internalUserClusterClientImpl: (() => { - return Promise.reject(new Error('something bad happened')); - }) as TestOptions['internalUserClusterClientImpl'], + nodeSettingsResponse: async () => { + throw new Error('something bad happened'); + }, + xpackUsageResponse: async () => { + throw new Error('something bad happened'); + }, asserts: { statusCode: 200, result: { diff --git a/x-pack/plugins/security/server/routes/role_mapping/feature_check.ts b/x-pack/plugins/security/server/routes/role_mapping/feature_check.ts index 88c7f193cea34..470039b8ae92b 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/feature_check.ts +++ b/x-pack/plugins/security/server/routes/role_mapping/feature_check.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Logger, ILegacyClusterClient } from 'src/core/server'; +import { ElasticsearchClient, Logger } from 'src/core/server'; import { createLicensedRouteHandler } from '../licensed_route_handler'; import { RouteDefinitionParams } from '..'; @@ -34,24 +34,18 @@ interface XPackUsageResponse { const INCOMPATIBLE_REALMS = ['file', 'native']; -export function defineRoleMappingFeatureCheckRoute({ - router, - clusterClient, - logger, -}: RouteDefinitionParams) { +export function defineRoleMappingFeatureCheckRoute({ router, logger }: RouteDefinitionParams) { router.get( { path: '/internal/security/_check_role_mapping_features', validate: false, }, createLicensedRouteHandler(async (context, request, response) => { - const { has_all_requested: canManageRoleMappings } = await clusterClient - .asScoped(request) - .callAsCurrentUser('shield.hasPrivileges', { - body: { - cluster: ['manage_security'], - }, - }); + const { + body: { has_all_requested: canManageRoleMappings }, + } = await context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges<{ + has_all_requested: boolean; + }>({ body: { cluster: ['manage_security'] } }); if (!canManageRoleMappings) { return response.ok({ @@ -61,7 +55,10 @@ export function defineRoleMappingFeatureCheckRoute({ }); } - const enabledFeatures = await getEnabledRoleMappingsFeatures(clusterClient, logger); + const enabledFeatures = await getEnabledRoleMappingsFeatures( + context.core.elasticsearch.client.asInternalUser, + logger + ); return response.ok({ body: { @@ -73,13 +70,12 @@ export function defineRoleMappingFeatureCheckRoute({ ); } -async function getEnabledRoleMappingsFeatures(clusterClient: ILegacyClusterClient, logger: Logger) { +async function getEnabledRoleMappingsFeatures(esClient: ElasticsearchClient, logger: Logger) { logger.debug(`Retrieving role mappings features`); - const nodeScriptSettingsPromise: Promise = clusterClient - .callAsInternalUser('nodes.info', { - filterPath: 'nodes.*.settings.script', - }) + const nodeScriptSettingsPromise = esClient.nodes + .info({ filter_path: 'nodes.*.settings.script' }) + .then(({ body }) => body) .catch((error) => { // fall back to assuming that node settings are unset/at their default values. // this will allow the role mappings UI to permit both role template script types, @@ -88,13 +84,11 @@ async function getEnabledRoleMappingsFeatures(clusterClient: ILegacyClusterClien return {}; }); - const xpackUsagePromise: Promise = clusterClient - // `transport.request` is potentially unsafe when combined with untrusted user input. - // Do not augment with such input. - .callAsInternalUser('transport.request', { - method: 'GET', - path: '/_xpack/usage', - }) + // `transport.request` is potentially unsafe when combined with untrusted user input. + // Do not augment with such input. + const xpackUsagePromise = esClient.transport + .request({ method: 'GET', path: '/_xpack/usage' }) + .then(({ body }) => body as XPackUsageResponse) .catch((error) => { // fall back to no external realms configured. // this will cause a warning in the UI about no compatible realms being enabled, but will otherwise allow diff --git a/x-pack/plugins/security/server/routes/role_mapping/get.test.ts b/x-pack/plugins/security/server/routes/role_mapping/get.test.ts index 2519034b386bf..625aae42a3907 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/get.test.ts +++ b/x-pack/plugins/security/server/routes/role_mapping/get.test.ts @@ -6,9 +6,9 @@ import Boom from '@hapi/boom'; import { routeDefinitionParamsMock } from '../index.mock'; -import { elasticsearchServiceMock, httpServerMock } from 'src/core/server/mocks'; +import { coreMock, httpServerMock } from 'src/core/server/mocks'; import { defineRoleMappingGetRoutes } from './get'; -import { kibanaResponseFactory, RequestHandlerContext } from '../../../../../../src/core/server'; +import { kibanaResponseFactory } from '../../../../../../src/core/server'; const mockRoleMappingResponse = { mapping1: { @@ -49,13 +49,22 @@ const mockRoleMappingResponse = { }, }; +function getMockContext( + licenseCheckResult: { state: string; message?: string } = { state: 'valid' } +) { + return { + core: coreMock.createRequestHandlerContext(), + licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } } as any, + }; +} + describe('GET role mappings', () => { it('returns all role mappings', async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); - - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockRouteDefinitionParams.clusterClient.asScoped.mockReturnValue(mockScopedClusterClient); - mockScopedClusterClient.callAsCurrentUser.mockResolvedValue(mockRoleMappingResponse); + const mockContext = getMockContext(); + mockContext.core.elasticsearch.client.asCurrentUser.security.getRoleMapping.mockResolvedValue({ + body: mockRoleMappingResponse, + } as any); defineRoleMappingGetRoutes(mockRouteDefinitionParams); @@ -67,11 +76,6 @@ describe('GET role mappings', () => { path: `/internal/security/role_mapping`, headers, }); - const mockContext = ({ - licensing: { - license: { check: jest.fn().mockReturnValue({ state: 'valid' }) }, - }, - } as unknown) as RequestHandlerContext; const response = await handler(mockContext, mockRequest, kibanaResponseFactory); expect(response.status).toBe(200); @@ -118,29 +122,27 @@ describe('GET role mappings', () => { }, ]); - expect(mockRouteDefinitionParams.clusterClient.asScoped).toHaveBeenCalledWith(mockRequest); - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith( - 'shield.getRoleMappings', - { name: undefined } - ); + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.getRoleMapping + ).toHaveBeenCalledWith({ name: undefined }); }); it('returns role mapping by name', async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); - - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockRouteDefinitionParams.clusterClient.asScoped.mockReturnValue(mockScopedClusterClient); - mockScopedClusterClient.callAsCurrentUser.mockResolvedValue({ - mapping1: { - enabled: true, - roles: ['foo', 'bar'], - rules: { - field: { - dn: 'CN=bob,OU=example,O=com', + const mockContext = getMockContext(); + mockContext.core.elasticsearch.client.asCurrentUser.security.getRoleMapping.mockResolvedValue({ + body: { + mapping1: { + enabled: true, + roles: ['foo', 'bar'], + rules: { + field: { + dn: 'CN=bob,OU=example,O=com', + }, }, }, }, - }); + } as any); defineRoleMappingGetRoutes(mockRouteDefinitionParams); @@ -155,11 +157,6 @@ describe('GET role mappings', () => { params: { name }, headers, }); - const mockContext = ({ - licensing: { - license: { check: jest.fn().mockReturnValue({ state: 'valid' }) }, - }, - } as unknown) as RequestHandlerContext; const response = await handler(mockContext, mockRequest, kibanaResponseFactory); expect(response.status).toBe(200); @@ -175,16 +172,15 @@ describe('GET role mappings', () => { }, }); - expect(mockRouteDefinitionParams.clusterClient.asScoped).toHaveBeenCalledWith(mockRequest); - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith( - 'shield.getRoleMappings', - { name } - ); + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.getRoleMapping + ).toHaveBeenCalledWith({ name }); }); describe('failure', () => { it('returns result of license check', async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); + const mockContext = getMockContext({ state: 'invalid', message: 'test forbidden message' }); defineRoleMappingGetRoutes(mockRouteDefinitionParams); @@ -196,29 +192,19 @@ describe('GET role mappings', () => { path: `/internal/security/role_mapping`, headers, }); - const mockContext = ({ - licensing: { - license: { - check: jest.fn().mockReturnValue({ - state: 'invalid', - message: 'test forbidden message', - }), - }, - }, - } as unknown) as RequestHandlerContext; const response = await handler(mockContext, mockRequest, kibanaResponseFactory); expect(response.status).toBe(403); expect(response.payload).toEqual({ message: 'test forbidden message' }); - expect(mockRouteDefinitionParams.clusterClient.asScoped).not.toHaveBeenCalled(); + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.getRoleMapping + ).not.toHaveBeenCalled(); }); it('returns a 404 when the role mapping is not found', async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); - - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockRouteDefinitionParams.clusterClient.asScoped.mockReturnValue(mockScopedClusterClient); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue( + const mockContext = getMockContext(); + mockContext.core.elasticsearch.client.asCurrentUser.security.getRoleMapping.mockRejectedValue( Boom.notFound('role mapping not found!') ); @@ -235,18 +221,12 @@ describe('GET role mappings', () => { params: { name }, headers, }); - const mockContext = ({ - licensing: { - license: { check: jest.fn().mockReturnValue({ state: 'valid' }) }, - }, - } as unknown) as RequestHandlerContext; const response = await handler(mockContext, mockRequest, kibanaResponseFactory); expect(response.status).toBe(404); - expect(mockRouteDefinitionParams.clusterClient.asScoped).toHaveBeenCalledWith(mockRequest); expect( - mockScopedClusterClient.callAsCurrentUser - ).toHaveBeenCalledWith('shield.getRoleMappings', { name }); + mockContext.core.elasticsearch.client.asCurrentUser.security.getRoleMapping + ).toHaveBeenCalledWith({ name }); }); }); }); diff --git a/x-pack/plugins/security/server/routes/role_mapping/get.ts b/x-pack/plugins/security/server/routes/role_mapping/get.ts index 63598584b5d1b..5ab9b1f6b4a24 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/get.ts +++ b/x-pack/plugins/security/server/routes/role_mapping/get.ts @@ -14,7 +14,7 @@ interface RoleMappingsResponse { } export function defineRoleMappingGetRoutes(params: RouteDefinitionParams) { - const { clusterClient, logger, router } = params; + const { logger, router } = params; router.get( { @@ -29,13 +29,11 @@ export function defineRoleMappingGetRoutes(params: RouteDefinitionParams) { const expectSingleEntity = typeof request.params.name === 'string'; try { - const roleMappingsResponse: RoleMappingsResponse = await clusterClient - .asScoped(request) - .callAsCurrentUser('shield.getRoleMappings', { - name: request.params.name, - }); + const roleMappingsResponse = await context.core.elasticsearch.client.asCurrentUser.security.getRoleMapping( + { name: request.params.name } + ); - const mappings = Object.entries(roleMappingsResponse).map(([name, mapping]) => { + const mappings = Object.entries(roleMappingsResponse.body).map(([name, mapping]) => { return { name, ...mapping, diff --git a/x-pack/plugins/security/server/routes/role_mapping/post.test.ts b/x-pack/plugins/security/server/routes/role_mapping/post.test.ts index 8f61d2a122f0c..5dc7a21a02c6f 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/post.test.ts +++ b/x-pack/plugins/security/server/routes/role_mapping/post.test.ts @@ -5,17 +5,20 @@ */ import { routeDefinitionParamsMock } from '../index.mock'; -import { elasticsearchServiceMock, httpServerMock } from 'src/core/server/mocks'; -import { kibanaResponseFactory, RequestHandlerContext } from '../../../../../../src/core/server'; +import { coreMock, httpServerMock } from 'src/core/server/mocks'; +import { kibanaResponseFactory } from '../../../../../../src/core/server'; import { defineRoleMappingPostRoutes } from './post'; describe('POST role mappings', () => { it('allows a role mapping to be created', async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); - - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockRouteDefinitionParams.clusterClient.asScoped.mockReturnValue(mockScopedClusterClient); - mockScopedClusterClient.callAsCurrentUser.mockResolvedValue({ created: true }); + const mockContext = { + core: coreMock.createRequestHandlerContext(), + licensing: { license: { check: jest.fn().mockReturnValue({ state: 'valid' }) } } as any, + }; + mockContext.core.elasticsearch.client.asCurrentUser.security.putRoleMapping.mockResolvedValue({ + body: { created: true }, + } as any); defineRoleMappingPostRoutes(mockRouteDefinitionParams); @@ -39,37 +42,41 @@ describe('POST role mappings', () => { }, headers, }); - const mockContext = ({ - licensing: { - license: { check: jest.fn().mockReturnValue({ state: 'valid' }) }, - }, - } as unknown) as RequestHandlerContext; const response = await handler(mockContext, mockRequest, kibanaResponseFactory); expect(response.status).toBe(200); expect(response.payload).toEqual({ created: true }); - expect(mockRouteDefinitionParams.clusterClient.asScoped).toHaveBeenCalledWith(mockRequest); - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith( - 'shield.saveRoleMapping', - { - name, - body: { - enabled: true, - roles: ['foo', 'bar'], - rules: { - field: { - dn: 'CN=bob,OU=example,O=com', - }, + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.putRoleMapping + ).toHaveBeenCalledWith({ + name, + body: { + enabled: true, + roles: ['foo', 'bar'], + rules: { + field: { + dn: 'CN=bob,OU=example,O=com', }, }, - } - ); + }, + }); }); describe('failure', () => { it('returns result of license check', async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); + const mockContext = { + core: coreMock.createRequestHandlerContext(), + licensing: { + license: { + check: jest.fn().mockReturnValue({ + state: 'invalid', + message: 'test forbidden message', + }), + }, + } as any, + }; defineRoleMappingPostRoutes(mockRouteDefinitionParams); @@ -81,22 +88,14 @@ describe('POST role mappings', () => { path: `/internal/security/role_mapping`, headers, }); - const mockContext = ({ - licensing: { - license: { - check: jest.fn().mockReturnValue({ - state: 'invalid', - message: 'test forbidden message', - }), - }, - }, - } as unknown) as RequestHandlerContext; const response = await handler(mockContext, mockRequest, kibanaResponseFactory); expect(response.status).toBe(403); expect(response.payload).toEqual({ message: 'test forbidden message' }); - expect(mockRouteDefinitionParams.clusterClient.asScoped).not.toHaveBeenCalled(); + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.putRoleMapping + ).not.toHaveBeenCalled(); }); }); }); diff --git a/x-pack/plugins/security/server/routes/role_mapping/post.ts b/x-pack/plugins/security/server/routes/role_mapping/post.ts index 11149f38069a7..6c1b19dacb601 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/post.ts +++ b/x-pack/plugins/security/server/routes/role_mapping/post.ts @@ -8,9 +8,7 @@ import { createLicensedRouteHandler } from '../licensed_route_handler'; import { wrapError } from '../../errors'; import { RouteDefinitionParams } from '..'; -export function defineRoleMappingPostRoutes(params: RouteDefinitionParams) { - const { clusterClient, router } = params; - +export function defineRoleMappingPostRoutes({ router }: RouteDefinitionParams) { router.post( { path: '/internal/security/role_mapping/{name}', @@ -43,13 +41,10 @@ export function defineRoleMappingPostRoutes(params: RouteDefinitionParams) { }, createLicensedRouteHandler(async (context, request, response) => { try { - const saveResponse = await clusterClient - .asScoped(request) - .callAsCurrentUser('shield.saveRoleMapping', { - name: request.params.name, - body: request.body, - }); - return response.ok({ body: saveResponse }); + const saveResponse = await context.core.elasticsearch.client.asCurrentUser.security.putRoleMapping( + { name: request.params.name, body: request.body } + ); + return response.ok({ body: saveResponse.body }); } catch (error) { const wrappedError = wrapError(error); return response.customError({ diff --git a/x-pack/plugins/security/server/routes/users/change_password.test.ts b/x-pack/plugins/security/server/routes/users/change_password.test.ts index c66b5f985cb33..d98c0acb7d86d 100644 --- a/x-pack/plugins/security/server/routes/users/change_password.test.ts +++ b/x-pack/plugins/security/server/routes/users/change_password.test.ts @@ -7,21 +7,20 @@ import { errors } from 'elasticsearch'; import { ObjectType } from '@kbn/config-schema'; import type { PublicMethodsOf } from '@kbn/utility-types'; +import type { DeeplyMockedKeys } from '@kbn/utility-types/jest'; import { - ILegacyClusterClient, + Headers, IRouter, - ILegacyScopedClusterClient, kibanaResponseFactory, RequestHandler, RequestHandlerContext, RouteConfig, - ScopeableRequest, } from '../../../../../../src/core/server'; import { Authentication, AuthenticationResult } from '../../authentication'; import { Session } from '../../session_management'; import { defineChangeUserPasswordRoutes } from './change_password'; -import { elasticsearchServiceMock, httpServerMock } from '../../../../../../src/core/server/mocks'; +import { coreMock, httpServerMock } from '../../../../../../src/core/server/mocks'; import { mockAuthenticatedUser } from '../../../common/model/authenticated_user.mock'; import { sessionMock } from '../../session_management/session.mock'; import { routeDefinitionParamsMock } from '../index.mock'; @@ -30,19 +29,19 @@ describe('Change password', () => { let router: jest.Mocked; let authc: jest.Mocked; let session: jest.Mocked>; - let mockClusterClient: jest.Mocked; - let mockScopedClusterClient: jest.Mocked; let routeHandler: RequestHandler; let routeConfig: RouteConfig; - let mockContext: RequestHandlerContext; - - function checkPasswordChangeAPICall(username: string, request: ScopeableRequest) { - expect(mockClusterClient.asScoped).toHaveBeenCalledTimes(1); - expect(mockClusterClient.asScoped).toHaveBeenCalledWith(request); - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledTimes(1); - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith( - 'shield.changePassword', - { username, body: { password: 'new-password' } } + let mockContext: DeeplyMockedKeys; + + function checkPasswordChangeAPICall(username: string, headers?: Headers) { + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.changePassword + ).toHaveBeenCalledTimes(1); + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.changePassword + ).toHaveBeenCalledWith( + { username, body: { password: 'new-password' } }, + headers && { headers } ); } @@ -56,15 +55,10 @@ describe('Change password', () => { authc.login.mockResolvedValue(AuthenticationResult.succeeded(mockAuthenticatedUser())); session.get.mockResolvedValue(sessionMock.createValue()); - mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockClusterClient = routeParamsMock.clusterClient; - mockClusterClient.asScoped.mockReturnValue(mockScopedClusterClient); - - mockContext = ({ - licensing: { - license: { check: jest.fn().mockReturnValue({ check: 'valid' }) }, - }, - } as unknown) as RequestHandlerContext; + mockContext = { + core: coreMock.createRequestHandlerContext(), + licensing: { license: { check: jest.fn().mockReturnValue({ state: 'valid' }) } }, + } as any; defineChangeUserPasswordRoutes(routeParamsMock); @@ -114,20 +108,18 @@ describe('Change password', () => { const changePasswordFailure = new (errors.AuthenticationException as any)('Unauthorized', { body: { error: { header: { 'WWW-Authenticate': 'Negotiate' } } }, }); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue(changePasswordFailure); + mockContext.core.elasticsearch.client.asCurrentUser.security.changePassword.mockRejectedValue( + changePasswordFailure + ); const response = await routeHandler(mockContext, mockRequest, kibanaResponseFactory); expect(response.status).toBe(403); expect(response.payload).toEqual(changePasswordFailure); - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledTimes(1); - expect(mockClusterClient.asScoped).toHaveBeenCalledTimes(1); - expect(mockClusterClient.asScoped).toHaveBeenCalledWith({ - headers: { - ...mockRequest.headers, - authorization: `Basic ${Buffer.from(`${username}:old-password`).toString('base64')}`, - }, + checkPasswordChangeAPICall(username, { + ...mockRequest.headers, + authorization: `Basic ${Buffer.from(`${username}:old-password`).toString('base64')}`, }); }); @@ -148,16 +140,16 @@ describe('Change password', () => { expect(response.payload).toEqual(loginFailureReason); checkPasswordChangeAPICall(username, { - headers: { - ...mockRequest.headers, - authorization: `Basic ${Buffer.from(`${username}:old-password`).toString('base64')}`, - }, + ...mockRequest.headers, + authorization: `Basic ${Buffer.from(`${username}:old-password`).toString('base64')}`, }); }); it('returns 500 if password update request fails with non-401 error.', async () => { const failureReason = new Error('Request failed.'); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue(failureReason); + mockContext.core.elasticsearch.client.asCurrentUser.security.changePassword.mockRejectedValue( + failureReason + ); const response = await routeHandler(mockContext, mockRequest, kibanaResponseFactory); @@ -165,10 +157,8 @@ describe('Change password', () => { expect(response.payload).toEqual(failureReason); checkPasswordChangeAPICall(username, { - headers: { - ...mockRequest.headers, - authorization: `Basic ${Buffer.from(`${username}:old-password`).toString('base64')}`, - }, + ...mockRequest.headers, + authorization: `Basic ${Buffer.from(`${username}:old-password`).toString('base64')}`, }); }); @@ -179,10 +169,8 @@ describe('Change password', () => { expect(response.payload).toBeUndefined(); checkPasswordChangeAPICall(username, { - headers: { - ...mockRequest.headers, - authorization: `Basic ${Buffer.from(`${username}:old-password`).toString('base64')}`, - }, + ...mockRequest.headers, + authorization: `Basic ${Buffer.from(`${username}:old-password`).toString('base64')}`, }); expect(authc.login).toHaveBeenCalledTimes(1); @@ -209,10 +197,8 @@ describe('Change password', () => { expect(response.payload).toBeUndefined(); checkPasswordChangeAPICall(username, { - headers: { - ...mockRequest.headers, - authorization: `Basic ${Buffer.from(`${username}:old-password`).toString('base64')}`, - }, + ...mockRequest.headers, + authorization: `Basic ${Buffer.from(`${username}:old-password`).toString('base64')}`, }); expect(authc.login).toHaveBeenCalledTimes(1); @@ -230,10 +216,8 @@ describe('Change password', () => { expect(response.payload).toBeUndefined(); checkPasswordChangeAPICall(username, { - headers: { - ...mockRequest.headers, - authorization: `Basic ${Buffer.from(`${username}:old-password`).toString('base64')}`, - }, + ...mockRequest.headers, + authorization: `Basic ${Buffer.from(`${username}:old-password`).toString('base64')}`, }); expect(authc.login).not.toHaveBeenCalled(); @@ -249,7 +233,9 @@ describe('Change password', () => { it('returns 500 if password update request fails.', async () => { const failureReason = new Error('Request failed.'); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue(failureReason); + mockContext.core.elasticsearch.client.asCurrentUser.security.changePassword.mockRejectedValue( + failureReason + ); const response = await routeHandler(mockContext, mockRequest, kibanaResponseFactory); @@ -257,7 +243,7 @@ describe('Change password', () => { expect(response.payload).toEqual(failureReason); expect(authc.login).not.toHaveBeenCalled(); - checkPasswordChangeAPICall(username, mockRequest); + checkPasswordChangeAPICall(username); }); it('successfully changes user password.', async () => { @@ -267,7 +253,7 @@ describe('Change password', () => { expect(response.payload).toBeUndefined(); expect(authc.login).not.toHaveBeenCalled(); - checkPasswordChangeAPICall(username, mockRequest); + checkPasswordChangeAPICall(username); }); }); }); diff --git a/x-pack/plugins/security/server/routes/users/change_password.ts b/x-pack/plugins/security/server/routes/users/change_password.ts index be868f841eeeb..66d36b4294883 100644 --- a/x-pack/plugins/security/server/routes/users/change_password.ts +++ b/x-pack/plugins/security/server/routes/users/change_password.ts @@ -14,12 +14,7 @@ import { } from '../../authentication'; import { RouteDefinitionParams } from '..'; -export function defineChangeUserPasswordRoutes({ - authc, - session, - router, - clusterClient, -}: RouteDefinitionParams) { +export function defineChangeUserPasswordRoutes({ authc, session, router }: RouteDefinitionParams) { router.post( { path: '/internal/security/users/{username}/password', @@ -43,28 +38,26 @@ export function defineChangeUserPasswordRoutes({ // If user is changing their own password they should provide a proof of knowledge their // current password via sending it in `Authorization: Basic base64(username:current password)` // HTTP header no matter how they logged in to Kibana. - const scopedClusterClient = clusterClient.asScoped( - isUserChangingOwnPassword - ? { - headers: { - ...request.headers, - authorization: new HTTPAuthorizationHeader( - 'Basic', - new BasicHTTPAuthorizationHeaderCredentials( - username, - currentPassword || '' - ).toString() - ).toString(), - }, - } - : request - ); + const options = isUserChangingOwnPassword + ? { + headers: { + ...request.headers, + authorization: new HTTPAuthorizationHeader( + 'Basic', + new BasicHTTPAuthorizationHeaderCredentials( + username, + currentPassword || '' + ).toString() + ).toString(), + }, + } + : undefined; try { - await scopedClusterClient.callAsCurrentUser('shield.changePassword', { - username, - body: { password: newPassword }, - }); + await context.core.elasticsearch.client.asCurrentUser.security.changePassword( + { username, body: { password: newPassword } }, + options + ); } catch (error) { // This may happen only if user's credentials are rejected meaning that current password // isn't correct. diff --git a/x-pack/plugins/security/server/routes/users/create_or_update.ts b/x-pack/plugins/security/server/routes/users/create_or_update.ts index 5a3e50bb11d5c..a98848a583500 100644 --- a/x-pack/plugins/security/server/routes/users/create_or_update.ts +++ b/x-pack/plugins/security/server/routes/users/create_or_update.ts @@ -9,7 +9,7 @@ import { wrapIntoCustomErrorResponse } from '../../errors'; import { createLicensedRouteHandler } from '../licensed_route_handler'; import { RouteDefinitionParams } from '..'; -export function defineCreateOrUpdateUserRoutes({ router, clusterClient }: RouteDefinitionParams) { +export function defineCreateOrUpdateUserRoutes({ router }: RouteDefinitionParams) { router.post( { path: '/internal/security/users/{username}', @@ -28,7 +28,7 @@ export function defineCreateOrUpdateUserRoutes({ router, clusterClient }: RouteD }, createLicensedRouteHandler(async (context, request, response) => { try { - await clusterClient.asScoped(request).callAsCurrentUser('shield.putUser', { + await context.core.elasticsearch.client.asCurrentUser.security.putUser({ username: request.params.username, // Omit `username`, `enabled` and all fields with `null` value. body: Object.fromEntries( diff --git a/x-pack/plugins/security/server/routes/users/delete.ts b/x-pack/plugins/security/server/routes/users/delete.ts index 99a8d5c18ab3d..26a1765b4fbdf 100644 --- a/x-pack/plugins/security/server/routes/users/delete.ts +++ b/x-pack/plugins/security/server/routes/users/delete.ts @@ -9,7 +9,7 @@ import { RouteDefinitionParams } from '../index'; import { wrapIntoCustomErrorResponse } from '../../errors'; import { createLicensedRouteHandler } from '../licensed_route_handler'; -export function defineDeleteUserRoutes({ router, clusterClient }: RouteDefinitionParams) { +export function defineDeleteUserRoutes({ router }: RouteDefinitionParams) { router.delete( { path: '/internal/security/users/{username}', @@ -19,9 +19,9 @@ export function defineDeleteUserRoutes({ router, clusterClient }: RouteDefinitio }, createLicensedRouteHandler(async (context, request, response) => { try { - await clusterClient - .asScoped(request) - .callAsCurrentUser('shield.deleteUser', { username: request.params.username }); + await context.core.elasticsearch.client.asCurrentUser.security.deleteUser({ + username: request.params.username, + }); return response.noContent(); } catch (error) { diff --git a/x-pack/plugins/security/server/routes/users/get.ts b/x-pack/plugins/security/server/routes/users/get.ts index 0867910372546..aa6a4f6be8bad 100644 --- a/x-pack/plugins/security/server/routes/users/get.ts +++ b/x-pack/plugins/security/server/routes/users/get.ts @@ -9,7 +9,7 @@ import { wrapIntoCustomErrorResponse } from '../../errors'; import { createLicensedRouteHandler } from '../licensed_route_handler'; import { RouteDefinitionParams } from '..'; -export function defineGetUserRoutes({ router, clusterClient }: RouteDefinitionParams) { +export function defineGetUserRoutes({ router }: RouteDefinitionParams) { router.get( { path: '/internal/security/users/{username}', @@ -20,9 +20,13 @@ export function defineGetUserRoutes({ router, clusterClient }: RouteDefinitionPa createLicensedRouteHandler(async (context, request, response) => { try { const username = request.params.username; - const users = (await clusterClient - .asScoped(request) - .callAsCurrentUser('shield.getUser', { username })) as Record; + const { + body: users, + } = await context.core.elasticsearch.client.asCurrentUser.security.getUser< + Record + >({ + username, + }); if (!users[username]) { return response.notFound(); diff --git a/x-pack/plugins/security/server/routes/users/get_all.ts b/x-pack/plugins/security/server/routes/users/get_all.ts index 492ab27ab27ad..3c5ca184f747c 100644 --- a/x-pack/plugins/security/server/routes/users/get_all.ts +++ b/x-pack/plugins/security/server/routes/users/get_all.ts @@ -8,7 +8,7 @@ import { RouteDefinitionParams } from '../index'; import { wrapIntoCustomErrorResponse } from '../../errors'; import { createLicensedRouteHandler } from '../licensed_route_handler'; -export function defineGetAllUsersRoutes({ router, clusterClient }: RouteDefinitionParams) { +export function defineGetAllUsersRoutes({ router }: RouteDefinitionParams) { router.get( { path: '/internal/security/users', validate: false }, createLicensedRouteHandler(async (context, request, response) => { @@ -16,7 +16,7 @@ export function defineGetAllUsersRoutes({ router, clusterClient }: RouteDefiniti return response.ok({ // Return only values since keys (user names) are already duplicated there. body: Object.values( - await clusterClient.asScoped(request).callAsCurrentUser('shield.getUser') + (await context.core.elasticsearch.client.asCurrentUser.security.getUser()).body ), }); } catch (error) { From 0c623b6c017fc0addbdf18afb7b03e6639538020 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Wed, 2 Dec 2020 14:13:20 +0000 Subject: [PATCH 03/40] [Synthetics] Waterfall chart (#84199) * Add a Waterfall component - Adds a generic Waterfall component - Adds consumer consumption code for Synthetics --- .../monitor/synthetics/executed_journey.tsx | 54 +- .../monitor/synthetics/waterfall/README.md | 123 ++++ .../waterfall/components/constants.ts | 12 + .../waterfall/components/legend.tsx | 28 + .../components/middle_truncated_text.test.tsx | 39 + .../components/middle_truncated_text.tsx | 61 ++ .../waterfall/components/sidebar.tsx | 43 ++ .../synthetics/waterfall/components/styles.ts | 89 +++ .../waterfall/components/waterfall_chart.tsx | 194 +++++ .../synthetics/data_formatting.test.ts | 687 ++++++++++++++++++ .../consumers/synthetics/data_formatting.ts | 336 +++++++++ .../waterfall/consumers/synthetics/types.ts | 197 +++++ .../synthetics/waterfall_chart_wrapper.tsx | 84 +++ .../waterfall/context/waterfall_chart.tsx | 44 ++ .../monitor/synthetics/waterfall/index.tsx | 10 + .../monitor/synthetics/waterfall/types.ts | 21 + 16 files changed, 1996 insertions(+), 26 deletions(-) create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/README.md create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/constants.ts create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/legend.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/middle_truncated_text.test.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/middle_truncated_text.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/sidebar.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/data_formatting.test.ts create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/data_formatting.ts create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/types.ts create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/waterfall_chart_wrapper.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/index.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/types.ts diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx index 9a3e045017f9a..0c47e4c73e976 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx @@ -57,29 +57,31 @@ interface ExecutedJourneyProps { journey: JourneyState; } -export const ExecutedJourney: FC = ({ journey }) => ( -
- -

- -

-

- {statusMessage( - journey.steps - .filter(isStepEnd) - .reduce(reduceStepStatus, { failed: 0, skipped: 0, succeeded: 0 }) - )} -

-
- - - {journey.steps.filter(isStepEnd).map((step, index) => ( - - ))} - - -
-); +export const ExecutedJourney: FC = ({ journey }) => { + return ( +
+ +

+ +

+

+ {statusMessage( + journey.steps + .filter(isStepEnd) + .reduce(reduceStepStatus, { failed: 0, skipped: 0, succeeded: 0 }) + )} +

+
+ + + {journey.steps.filter(isStepEnd).map((step, index) => ( + + ))} + + +
+ ); +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/README.md b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/README.md new file mode 100644 index 0000000000000..cf8d3b5345eaa --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/README.md @@ -0,0 +1,123 @@ +# Waterfall chart + +## Introduction + +The waterfall chart component aims to be agnostic in it's approach, so that a variety of consumers / solutions can use it. Some of Elastic Chart's features are used in a non-standard way to facilitate this flexibility, this README aims to cover some of the things that might be less obvious, and also provides a high level overview of implementation. + +## Requirements for usage + +The waterfall chart component asssumes that the consumer is making use of `KibanaReactContext`, and as such things like `useKibana` can be called. + +Consumers are also expected to be using the `` so that the waterfall chart can apply styled-component styles based on the EUI theme. + +These are the two hard requirements, but almost all plugins will be using these. + +## Rendering + +At it's core the watefall chart is a stacked bar chart that has been rotated through 90 degrees. As such it's important to understand that `x` is now represented as `y` and vice versa. + +## Flexibility + +This section aims to cover some things that are non-standard. + +### Tooltip + +By default the formatting of tooltip values is very basic, but for a waterfall chart there needs to be a great deal of flexibility to represent whatever breakdown you're trying to show. + +As such a custom tooltip component is used. This custom component would usually only have access to some basic props that pertain to the values of the hovered bar. The waterfall chart component extends this by making us of a waterfall chart context. + +The custom tooltip component can use the context to access the full set of chart data, find the relevant items (those with the same `x` value) and call a custom `renderTooltipItem` for each item, `renderTooltipItem` will be passed `item.config.tooltipProps`. Every consumer can choose what they use for their `tooltipProps`. + +Some consumers might need colours, some might need iconography and so on. The waterfall chart doesn't make assumptions, and will render out the React content returned by `renderTooltipItem`. + +IMPORTANT: `renderTooltipItem` is provided via context and not as a direct prop due to the fact the custom tooltip component would usually only have access to the props provided directly to it from Elastic Charts. + +### Colours + +The easiest way to facilitate specific colours for each stack (let's say your colours are mapped to a constraint like mime type) is to assign the colour directly on your datum `config` property, and then access this directly in the `barStyleAccessor` function, e.g. + +``` +barStyleAccessor={(datum) => { + return datum.datum.config.colour; +}) +``` + +### Config + +The notion of `config` has been mentioned already. But this is a place that consumers can store their solution specific properties. `renderTooltipItem` will make use of `config.tooltipProps`, and `barStyleAccessor` can make use of anything on `config`. + +### Sticky top axis + +By default there is no "sticky" axis functionality in Elastic Charts, therefore a second chart is rendered, this contains a replica of the top axis, and renders one empty data point (as a chart can't only have an axis). This second chart is then positioned in such a way that it covers the top of the real axis, and remains fixed. + +## Data + +The waterfall chart expects data in a relatively simple format, there are the usual plot properties (`x`, `y0`, and `y`) and then `config`. E.g. + +``` +const series = [ + {x: 0, y: 0, y: 100, config: { tooltipProps: { type: 'dns' }}}, + {x: 0, y0: 300, y: 500, config: { tooltipProps: { type: 'ssl' }}}, + {x: 1, y0: 250, y: 300, config: { tooltipProps: { propA: 'somethingBreakdownRelated' }}}, + {x: 1, y0: 500, y: 600, config: { tooltipProps: { propA: 'anotherBreakdown' }}}, +] +``` + +Gaps in bars are fine, and to be expected for certain solutions. + +## Sidebar items + +The waterfall chart component again doesn't make assumptions about consumer's sidebar items' content, but the waterfall chart does handle the rendering so the sidebar can be aligned and rendered properly alongside the chart itself. + +`sidebarItems` should be provided to the context, and a `renderSidebarItem` prop should be provided to the chart. + +A sidebar is optional. + +There is a great deal of flexibility here so that solutions can make use of this in the way they need. For example, if you'd like to add a toggle functionality, so that clicking an item shows / hides it's children, this would involve rendering your toggle in `renderSidebarItem` and then when clicked you can handle adjusting your data as necessary. + +IMPORTANT: It is important to understand that the chart itself makes use of a fixed height. The sidebar will create a space that has a matching height. Each item is assigned equal space vertically via Flexbox, so that the items align with the relevant bar to the right (these are two totally different rendering contexts, with the chart itself sitting within a `canvas` element). So it's important that whatever content you choose to render here doesn't exceed the available height available to each item. The chart's height is calculated as `numberOfBars * 32`, so content should be kept within that `32px` threshold. + +## Legend items + +Much the same as with the sidebar items, no assumptions are made here, solutions will have different aims. + +`legendItems` should be provided to the context, and a `renderLegendItem` prop should be provided to the chart. + +A legend is optional. + +## Overall usage + +Pulling all of this together, things look like this (for a specific solution): + +``` +const renderSidebarItem: RenderItem = (item, index) => { + return ; +}; + +const renderLegendItem: RenderItem = (item) => { + return {item.name}; +}; + + { + return {tooltipProps.value}; + }} +> + `${Number(d).toFixed(0)} ms`} + domain={{ min: domain.min, max: domain.max }} + barStyleAccessor={(datum) => { + return datum.datum.config.colour; + }} + renderSidebarItem={renderSidebarItem} + renderLegendItem={renderLegendItem} + /> + +``` + +A solution could easily forego a sidebar and legend for a more minimalistic view, e.g. maybe a mini waterfall within a table column. + + diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/constants.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/constants.ts new file mode 100644 index 0000000000000..ac650c5ef0ddd --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/constants.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// Pixel value +export const BAR_HEIGHT = 32; +// Flex grow value +export const MAIN_GROW_SIZE = 8; +// Flex grow value +export const SIDEBAR_GROW_SIZE = 2; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/legend.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/legend.tsx new file mode 100644 index 0000000000000..85a205a7256f3 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/legend.tsx @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { IWaterfallContext } from '../context/waterfall_chart'; +import { WaterfallChartLegendContainer } from './styles'; +import { WaterfallChartProps } from './waterfall_chart'; + +interface LegendProps { + items: Required['legendItems']; + render: Required['renderLegendItem']; +} + +export const Legend: React.FC = ({ items, render }) => { + return ( + + + {items.map((item, index) => { + return {render(item, index)}; + })} + + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/middle_truncated_text.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/middle_truncated_text.test.tsx new file mode 100644 index 0000000000000..4f54a347d22d2 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/middle_truncated_text.test.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getChunks, MiddleTruncatedText } from './middle_truncated_text'; +import { shallowWithIntl } from '@kbn/test/jest'; +import React from 'react'; + +const longString = + 'this-is-a-really-really-really-really-really-really-really-really-long-string.madeup.extension'; + +describe('getChunks', () => { + it('Calculates chunks correctly', () => { + const result = getChunks(longString); + expect(result).toEqual({ + first: 'this-is-a-really-really-really-really-really-really-really-really-long-string.made', + last: 'up.extension', + }); + }); +}); + +describe('Component', () => { + it('Renders correctly', () => { + expect(shallowWithIntl()).toMatchInlineSnapshot(` + + + + this-is-a-really-really-really-really-really-really-really-really-long-string.made + + + up.extension + + + + `); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/middle_truncated_text.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/middle_truncated_text.tsx new file mode 100644 index 0000000000000..519927d7db28b --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/middle_truncated_text.tsx @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useMemo } from 'react'; +import styled from 'styled-components'; + +const OuterContainer = styled.div` + width: 100%; + height: 100%; + position: relative; +`; + +const InnerContainer = styled.div` + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + overflow: hidden; + display: flex; + min-width: 0; +`; // NOTE: min-width: 0 ensures flexbox and no-wrap children can co-exist + +const FirstChunk = styled.span` + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; +`; + +const LastChunk = styled.span` + flex-shrink: 0; +`; + +export const getChunks = (text: string) => { + const END_CHARS = 12; + const chars = text.split(''); + const splitPoint = chars.length - END_CHARS > 0 ? chars.length - END_CHARS : null; + const endChars = splitPoint ? chars.splice(splitPoint) : []; + return { first: chars.join(''), last: endChars.join('') }; +}; + +// Helper component for adding middle text truncation, e.g. +// really-really-really-long....ompressed.js +// Can be used to accomodate content in sidebar item rendering. +export const MiddleTruncatedText = ({ text }: { text: string }) => { + const chunks = useMemo(() => { + return getChunks(text); + }, [text]); + + return ( + + + {chunks.first} + {chunks.last} + + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/sidebar.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/sidebar.tsx new file mode 100644 index 0000000000000..9ff544fc1946b --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/sidebar.tsx @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiFlexItem } from '@elastic/eui'; +import { SIDEBAR_GROW_SIZE } from './constants'; +import { IWaterfallContext } from '../context/waterfall_chart'; +import { + WaterfallChartSidebarContainer, + WaterfallChartSidebarContainerInnerPanel, + WaterfallChartSidebarContainerFlexGroup, + WaterfallChartSidebarFlexItem, +} from './styles'; +import { WaterfallChartProps } from './waterfall_chart'; + +interface SidebarProps { + items: Required['sidebarItems']; + height: number; + render: Required['renderSidebarItem']; +} + +export const Sidebar: React.FC = ({ items, height, render }) => { + return ( + + + + + {items.map((item, index) => { + return ( + + {render(item, index)} + + ); + })} + + + + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts new file mode 100644 index 0000000000000..25f5e5f8f5cc9 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiPanel, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { euiStyled } from '../../../../../../../observability/public'; + +// NOTE: This isn't a perfect solution - changes in font size etc within charts could change the ideal height here. +const FIXED_AXIS_HEIGHT = 33; + +interface WaterfallChartOuterContainerProps { + height?: number; +} + +export const WaterfallChartOuterContainer = euiStyled.div` + height: ${(props) => (props.height ? `${props.height}px` : 'auto')}; + overflow-y: ${(props) => (props.height ? 'scroll' : 'visible')}; + overflow-x: hidden; +`; + +export const WaterfallChartFixedTopContainer = euiStyled.div` + position: sticky; + top: 0; + z-index: ${(props) => props.theme.eui.euiZLevel4}; +`; + +export const WaterfallChartFixedTopContainerSidebarCover = euiStyled(EuiPanel)` + height: 100%; + border-radius: 0 !important; + border: none; +`; // NOTE: border-radius !important is here as the "border" prop isn't working + +export const WaterfallChartFixedAxisContainer = euiStyled.div` + height: ${FIXED_AXIS_HEIGHT}px; +`; + +interface WaterfallChartSidebarContainer { + height: number; +} + +export const WaterfallChartSidebarContainer = euiStyled.div` + height: ${(props) => `${props.height - FIXED_AXIS_HEIGHT}px`}; + overflow-y: hidden; +`; + +export const WaterfallChartSidebarContainerInnerPanel = euiStyled(EuiPanel)` + border: 0; + height: 100%; +`; + +export const WaterfallChartSidebarContainerFlexGroup = euiStyled(EuiFlexGroup)` + height: 100%; +`; + +// Ensures flex items honour no-wrap of children, rather than trying to extend to the full width of children. +export const WaterfallChartSidebarFlexItem = euiStyled(EuiFlexItem)` + min-width: 0; + padding-left: ${(props) => props.theme.eui.paddingSizes.m}; + padding-right: ${(props) => props.theme.eui.paddingSizes.m}; +`; + +interface WaterfallChartChartContainer { + height: number; +} + +export const WaterfallChartChartContainer = euiStyled.div` + width: 100%; + height: ${(props) => `${props.height}px`}; + margin-top: -${FIXED_AXIS_HEIGHT}px; +`; + +export const WaterfallChartLegendContainer = euiStyled.div` + position: sticky; + bottom: 0; + z-index: ${(props) => props.theme.eui.euiZLevel4}; + background-color: ${(props) => props.theme.eui.euiColorLightestShade}; + padding: ${(props) => props.theme.eui.paddingSizes.xs}; + font-size: ${(props) => props.theme.eui.euiFontSizeXS}; + box-shadow: 0px -1px 4px 0px ${(props) => props.theme.eui.euiColorLightShade}; +`; // NOTE: EuiShadowColor is a little too dark to work with the background-color + +export const WaterfallChartTooltip = euiStyled.div` + background-color: ${(props) => props.theme.eui.euiColorDarkestShade}; + border-radius: ${(props) => props.theme.eui.euiBorderRadius}; + color: ${(props) => props.theme.eui.euiColorLightestShade}; + padding: ${(props) => props.theme.eui.paddingSizes.s}; +`; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx new file mode 100644 index 0000000000000..de4be0ea34b2c --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx @@ -0,0 +1,194 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useMemo } from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { + Axis, + BarSeries, + Chart, + Position, + ScaleType, + Settings, + TickFormatter, + DomainRange, + BarStyleAccessor, + TooltipInfo, + TooltipType, +} from '@elastic/charts'; +import { EUI_CHARTS_THEME_DARK, EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme'; +// NOTE: The WaterfallChart has a hard requirement that consumers / solutions are making use of KibanaReactContext, and useKibana etc +// can therefore be accessed. +import { useUiSetting$ } from '../../../../../../../../../src/plugins/kibana_react/public'; +import { useWaterfallContext } from '../context/waterfall_chart'; +import { + WaterfallChartOuterContainer, + WaterfallChartFixedTopContainer, + WaterfallChartFixedTopContainerSidebarCover, + WaterfallChartFixedAxisContainer, + WaterfallChartChartContainer, + WaterfallChartTooltip, +} from './styles'; +import { WaterfallData } from '../types'; +import { BAR_HEIGHT, MAIN_GROW_SIZE, SIDEBAR_GROW_SIZE } from './constants'; +import { Sidebar } from './sidebar'; +import { Legend } from './legend'; + +const Tooltip = ({ header }: TooltipInfo) => { + const { data, renderTooltipItem } = useWaterfallContext(); + const relevantItems = data.filter((item) => { + return item.x === header?.value; + }); + return ( + + + {relevantItems.map((item, index) => { + return ( + {renderTooltipItem(item.config.tooltipProps)} + ); + })} + + + ); +}; + +export type RenderItem = (item: I, index: number) => JSX.Element; + +export interface WaterfallChartProps { + tickFormat: TickFormatter; + domain: DomainRange; + barStyleAccessor: BarStyleAccessor; + renderSidebarItem?: RenderItem; + renderLegendItem?: RenderItem; + maxHeight?: number; +} + +const getUniqueBars = (data: WaterfallData) => { + return data.reduce>((acc, item) => { + if (!acc.has(item.x)) { + acc.add(item.x); + return acc; + } else { + return acc; + } + }, new Set()); +}; + +const getChartHeight = (data: WaterfallData): number => getUniqueBars(data).size * BAR_HEIGHT; + +export const WaterfallChart = ({ + tickFormat, + domain, + barStyleAccessor, + renderSidebarItem, + renderLegendItem, + maxHeight = 600, +}: WaterfallChartProps) => { + const { data, sidebarItems, legendItems } = useWaterfallContext(); + + const generatedHeight = useMemo(() => { + return getChartHeight(data); + }, [data]); + + const [darkMode] = useUiSetting$('theme:darkMode'); + + const theme = useMemo(() => { + return darkMode ? EUI_CHARTS_THEME_DARK.theme : EUI_CHARTS_THEME_LIGHT.theme; + }, [darkMode]); + + const shouldRenderSidebar = + sidebarItems && sidebarItems.length > 0 && renderSidebarItem ? true : false; + const shouldRenderLegend = + legendItems && legendItems.length > 0 && renderLegendItem ? true : false; + + return ( + + <> + + + {shouldRenderSidebar && ( + + + + )} + + + + + + + + ''} /> + + + + + + + + + {shouldRenderSidebar && ( + + )} + + + + + + + + ''} /> + + + + + + + {shouldRenderLegend && } + + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/data_formatting.test.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/data_formatting.test.ts new file mode 100644 index 0000000000000..698e6b4be0c4c --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/data_formatting.test.ts @@ -0,0 +1,687 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { colourPalette } from './data_formatting'; + +// const TEST_DATA = [ +// { +// '@timestamp': '2020-10-29T14:55:01.055Z', +// ecs: { +// version: '1.6.0', +// }, +// agent: { +// type: 'heartbeat', +// version: '7.10.0', +// hostname: 'docker-desktop', +// ephemeral_id: '34179df8-f97c-46a2-9e73-33976d4ac58d', +// id: '5a03ad5f-cc18-43e8-8f82-6b08b9ceb36a', +// name: 'docker-desktop', +// }, +// synthetics: { +// index: 7, +// payload: { +// request: { +// url: 'https://unpkg.com/director@1.2.8/build/director.js', +// method: 'GET', +// headers: { +// referer: '', +// user_agent: +// 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/88.0.4287.0 Safari/537.36', +// }, +// mixed_content_type: 'none', +// initial_priority: 'High', +// referrer_policy: 'no-referrer-when-downgrade', +// }, +// status: 200, +// method: 'GET', +// end: 13902.944973, +// url: 'https://unpkg.com/director@1.2.8/build/director.js', +// type: 'Script', +// is_navigation_request: false, +// start: 13902.752946, +// response: { +// encoded_data_length: 179, +// protocol: 'h2', +// headers: { +// content_encoding: 'br', +// server: 'cloudflare', +// age: '94838', +// cf_cache_status: 'HIT', +// x_content_type_options: 'nosniff', +// last_modified: 'Wed, 04 Feb 2015 03:25:28 GMT', +// cf_ray: '5e9dbc2bdda2e5a7-MAN', +// content_type: 'application/javascript; charset=utf-8', +// x_cloud_trace_context: 'eec7acc7a6f96b5353ef0d648bf437ac', +// expect_ct: +// 'max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"', +// access_control_allow_origin: '*', +// vary: 'Accept-Encoding', +// cache_control: 'public, max-age=31536000', +// date: 'Thu, 29 Oct 2020 14:55:00 GMT', +// cf_request_id: '061673ef6b0000e5a7cd07a000000001', +// etag: 'W/"4f70-NHpXdyWxnckEaeiXalAnXQ+oh4Q"', +// strict_transport_security: 'max-age=31536000; includeSubDomains; preload', +// }, +// remote_i_p_address: '104.16.125.175', +// connection_reused: true, +// timing: { +// dns_start: -1, +// push_end: 0, +// worker_fetch_start: -1, +// worker_respond_with_settled: -1, +// proxy_end: -1, +// worker_start: -1, +// worker_ready: -1, +// send_end: 158.391, +// connect_end: -1, +// connect_start: -1, +// send_start: 157.876, +// proxy_start: -1, +// push_start: 0, +// ssl_end: -1, +// receive_headers_end: 186.885, +// ssl_start: -1, +// request_time: 13902.757525, +// dns_end: -1, +// }, +// connection_id: 17, +// status_text: '', +// remote_port: 443, +// status: 200, +// security_details: { +// valid_to: 1627905600, +// certificate_id: 0, +// key_exchange_group: 'X25519', +// valid_from: 1596326400, +// protocol: 'TLS 1.3', +// issuer: 'Cloudflare Inc ECC CA-3', +// key_exchange: '', +// san_list: ['unpkg.com', '*.unpkg.com', 'sni.cloudflaressl.com'], +// signed_certificate_timestamp_list: [], +// certificate_transparency_compliance: 'unknown', +// cipher: 'AES_128_GCM', +// subject_name: 'sni.cloudflaressl.com', +// }, +// mime_type: 'application/javascript', +// url: 'https://unpkg.com/director@1.2.8/build/director.js', +// from_prefetch_cache: false, +// from_disk_cache: false, +// security_state: 'secure', +// response_time: 1.603983300513211e12, +// from_service_worker: false, +// }, +// }, +// journey: { +// name: 'check that title is present', +// id: 'check that title is present', +// }, +// type: 'journey/network_info', +// package_version: '0.0.1', +// }, +// monitor: { +// status: 'up', +// duration: { +// us: 24, +// }, +// id: 'check that title is present', +// name: 'check that title is present', +// type: 'browser', +// timespan: { +// gte: '2020-10-29T14:55:01.055Z', +// lt: '2020-10-29T14:56:01.055Z', +// }, +// check_group: '948d3b6b-19f6-11eb-b237-025000000001', +// }, +// event: { +// dataset: 'uptime', +// }, +// }, +// { +// '@timestamp': '2020-10-29T14:55:01.055Z', +// ecs: { +// version: '1.6.0', +// }, +// agent: { +// version: '7.10.0', +// hostname: 'docker-desktop', +// ephemeral_id: '34179df8-f97c-46a2-9e73-33976d4ac58d', +// id: '5a03ad5f-cc18-43e8-8f82-6b08b9ceb36a', +// name: 'docker-desktop', +// type: 'heartbeat', +// }, +// monitor: { +// check_group: '948d3b6b-19f6-11eb-b237-025000000001', +// status: 'up', +// duration: { +// us: 13, +// }, +// id: 'check that title is present', +// name: 'check that title is present', +// type: 'browser', +// timespan: { +// gte: '2020-10-29T14:55:01.055Z', +// lt: '2020-10-29T14:56:01.055Z', +// }, +// }, +// synthetics: { +// journey: { +// name: 'check that title is present', +// id: 'check that title is present', +// }, +// type: 'journey/network_info', +// package_version: '0.0.1', +// index: 9, +// payload: { +// start: 13902.76168, +// url: 'file:///opt/examples/todos/app/app.js', +// method: 'GET', +// is_navigation_request: false, +// end: 13902.770133, +// request: { +// headers: { +// referer: '', +// user_agent: +// 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/88.0.4287.0 Safari/537.36', +// }, +// mixed_content_type: 'none', +// initial_priority: 'High', +// referrer_policy: 'no-referrer-when-downgrade', +// url: 'file:///opt/examples/todos/app/app.js', +// method: 'GET', +// }, +// status: 0, +// type: 'Script', +// response: { +// protocol: 'file', +// connection_reused: false, +// mime_type: 'text/javascript', +// security_state: 'secure', +// from_disk_cache: false, +// url: 'file:///opt/examples/todos/app/app.js', +// status_text: '', +// connection_id: 0, +// from_prefetch_cache: false, +// encoded_data_length: -1, +// headers: {}, +// status: 0, +// from_service_worker: false, +// }, +// }, +// }, +// event: { +// dataset: 'uptime', +// }, +// }, +// { +// '@timestamp': '2020-10-29T14:55:01.000Z', +// monitor: { +// timespan: { +// lt: '2020-10-29T14:56:01.000Z', +// gte: '2020-10-29T14:55:01.000Z', +// }, +// id: 'check that title is present', +// name: 'check that title is present', +// check_group: '948d3b6b-19f6-11eb-b237-025000000001', +// status: 'up', +// duration: { +// us: 44365, +// }, +// type: 'browser', +// }, +// synthetics: { +// journey: { +// id: 'check that title is present', +// name: 'check that title is present', +// }, +// type: 'journey/network_info', +// package_version: '0.0.1', +// index: 5, +// payload: { +// status: 0, +// url: 'file:///opt/examples/todos/app/index.html', +// end: 13902.730261, +// request: { +// method: 'GET', +// headers: {}, +// mixed_content_type: 'none', +// initial_priority: 'VeryHigh', +// referrer_policy: 'no-referrer-when-downgrade', +// url: 'file:///opt/examples/todos/app/index.html', +// }, +// method: 'GET', +// response: { +// status: 0, +// connection_id: 0, +// from_disk_cache: false, +// headers: {}, +// encoded_data_length: -1, +// status_text: '', +// from_service_worker: false, +// connection_reused: false, +// url: 'file:///opt/examples/todos/app/index.html', +// remote_port: 0, +// security_state: 'secure', +// protocol: 'file', +// mime_type: 'text/html', +// remote_i_p_address: '', +// from_prefetch_cache: false, +// }, +// start: 13902.726626, +// type: 'Document', +// is_navigation_request: true, +// }, +// }, +// event: { +// dataset: 'uptime', +// }, +// ecs: { +// version: '1.6.0', +// }, +// agent: { +// ephemeral_id: '34179df8-f97c-46a2-9e73-33976d4ac58d', +// id: '5a03ad5f-cc18-43e8-8f82-6b08b9ceb36a', +// name: 'docker-desktop', +// type: 'heartbeat', +// version: '7.10.0', +// hostname: 'docker-desktop', +// }, +// }, +// { +// '@timestamp': '2020-10-29T14:55:01.044Z', +// monitor: { +// type: 'browser', +// timespan: { +// lt: '2020-10-29T14:56:01.044Z', +// gte: '2020-10-29T14:55:01.044Z', +// }, +// check_group: '948d3b6b-19f6-11eb-b237-025000000001', +// status: 'up', +// duration: { +// us: 10524, +// }, +// id: 'check that title is present', +// name: 'check that title is present', +// }, +// synthetics: { +// package_version: '0.0.1', +// index: 6, +// payload: { +// status: 200, +// type: 'Stylesheet', +// url: 'https://unpkg.com/todomvc-app-css@2.0.4/index.css', +// method: 'GET', +// start: 13902.75266, +// is_navigation_request: false, +// end: 13902.943835, +// response: { +// remote_i_p_address: '104.16.125.175', +// response_time: 1.603983300511892e12, +// url: 'https://unpkg.com/todomvc-app-css@2.0.4/index.css', +// mime_type: 'text/css', +// protocol: 'h2', +// security_state: 'secure', +// encoded_data_length: 414, +// remote_port: 443, +// status_text: '', +// timing: { +// proxy_start: -1, +// worker_ready: -1, +// worker_fetch_start: -1, +// receive_headers_end: 189.169, +// worker_respond_with_settled: -1, +// connect_end: 160.311, +// worker_start: -1, +// send_start: 161.275, +// dns_start: 0.528, +// send_end: 161.924, +// ssl_end: 160.267, +// proxy_end: -1, +// ssl_start: 29.726, +// request_time: 13902.753988, +// dns_end: 5.212, +// push_end: 0, +// push_start: 0, +// connect_start: 5.212, +// }, +// connection_reused: false, +// from_service_worker: false, +// security_details: { +// san_list: ['unpkg.com', '*.unpkg.com', 'sni.cloudflaressl.com'], +// valid_from: 1596326400, +// cipher: 'AES_128_GCM', +// protocol: 'TLS 1.3', +// issuer: 'Cloudflare Inc ECC CA-3', +// valid_to: 1627905600, +// certificate_id: 0, +// key_exchange_group: 'X25519', +// certificate_transparency_compliance: 'unknown', +// key_exchange: '', +// subject_name: 'sni.cloudflaressl.com', +// signed_certificate_timestamp_list: [], +// }, +// connection_id: 17, +// status: 200, +// from_disk_cache: false, +// from_prefetch_cache: false, +// headers: { +// date: 'Thu, 29 Oct 2020 14:55:00 GMT', +// x_cloud_trace_context: '76a4f7b8be185f2ac9aa839de3d6f893', +// cache_control: 'public, max-age=31536000', +// expect_ct: +// 'max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"', +// content_type: 'text/css; charset=utf-8', +// age: '627638', +// x_content_type_options: 'nosniff', +// last_modified: 'Sat, 09 Jan 2016 00:57:37 GMT', +// access_control_allow_origin: '*', +// cf_request_id: '061673ef6a0000e5a75a309000000001', +// vary: 'Accept-Encoding', +// strict_transport_security: 'max-age=31536000; includeSubDomains; preload', +// cf_ray: '5e9dbc2bdda1e5a7-MAN', +// content_encoding: 'br', +// etag: 'W/"1921-kYwbQVnRAA2V/L9Gr4SCtUE5LHQ"', +// server: 'cloudflare', +// cf_cache_status: 'HIT', +// }, +// }, +// request: { +// headers: { +// referer: '', +// user_agent: +// 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/88.0.4287.0 Safari/537.36', +// }, +// mixed_content_type: 'none', +// initial_priority: 'VeryHigh', +// referrer_policy: 'no-referrer-when-downgrade', +// url: 'https://unpkg.com/todomvc-app-css@2.0.4/index.css', +// method: 'GET', +// }, +// }, +// journey: { +// id: 'check that title is present', +// name: 'check that title is present', +// }, +// type: 'journey/network_info', +// }, +// event: { +// dataset: 'uptime', +// }, +// ecs: { +// version: '1.6.0', +// }, +// agent: { +// version: '7.10.0', +// hostname: 'docker-desktop', +// ephemeral_id: '34179df8-f97c-46a2-9e73-33976d4ac58d', +// id: '5a03ad5f-cc18-43e8-8f82-6b08b9ceb36a', +// name: 'docker-desktop', +// type: 'heartbeat', +// }, +// }, +// { +// '@timestamp': '2020-10-29T14:55:01.055Z', +// agent: { +// ephemeral_id: '34179df8-f97c-46a2-9e73-33976d4ac58d', +// id: '5a03ad5f-cc18-43e8-8f82-6b08b9ceb36a', +// name: 'docker-desktop', +// type: 'heartbeat', +// version: '7.10.0', +// hostname: 'docker-desktop', +// }, +// synthetics: { +// index: 8, +// payload: { +// method: 'GET', +// type: 'Script', +// response: { +// url: 'file:///opt/examples/todos/app/vue.min.js', +// protocol: 'file', +// connection_id: 0, +// headers: {}, +// mime_type: 'text/javascript', +// from_service_worker: false, +// status_text: '', +// connection_reused: false, +// encoded_data_length: -1, +// from_disk_cache: false, +// security_state: 'secure', +// from_prefetch_cache: false, +// status: 0, +// }, +// is_navigation_request: false, +// request: { +// mixed_content_type: 'none', +// initial_priority: 'High', +// referrer_policy: 'no-referrer-when-downgrade', +// url: 'file:///opt/examples/todos/app/vue.min.js', +// method: 'GET', +// headers: { +// referer: '', +// user_agent: +// 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/88.0.4287.0 Safari/537.36', +// }, +// }, +// end: 13902.772783, +// status: 0, +// start: 13902.760644, +// url: 'file:///opt/examples/todos/app/vue.min.js', +// }, +// journey: { +// name: 'check that title is present', +// id: 'check that title is present', +// }, +// type: 'journey/network_info', +// package_version: '0.0.1', +// }, +// monitor: { +// status: 'up', +// duration: { +// us: 82, +// }, +// name: 'check that title is present', +// type: 'browser', +// timespan: { +// gte: '2020-10-29T14:55:01.055Z', +// lt: '2020-10-29T14:56:01.055Z', +// }, +// id: 'check that title is present', +// check_group: '948d3b6b-19f6-11eb-b237-025000000001', +// }, +// event: { +// dataset: 'uptime', +// }, +// ecs: { +// version: '1.6.0', +// }, +// }, +// ]; + +// const toMillis = (seconds: number) => seconds * 1000; + +// describe('getTimings', () => { +// it('Calculates timings for network events correctly', () => { +// // NOTE: Uses these timings as the file protocol events don't have timing information +// const eventOneTimings = getTimings( +// TEST_DATA[0].synthetics.payload.response.timing!, +// toMillis(TEST_DATA[0].synthetics.payload.start), +// toMillis(TEST_DATA[0].synthetics.payload.end) +// ); +// expect(eventOneTimings).toEqual({ +// blocked: 162.4549999999106, +// connect: -1, +// dns: -1, +// receive: 0.5629999989271255, +// send: 0.5149999999999864, +// ssl: undefined, +// wait: 28.494, +// }); + +// const eventFourTimings = getTimings( +// TEST_DATA[3].synthetics.payload.response.timing!, +// toMillis(TEST_DATA[3].synthetics.payload.start), +// toMillis(TEST_DATA[3].synthetics.payload.end) +// ); +// expect(eventFourTimings).toEqual({ +// blocked: 1.8559999997466803, +// connect: 25.52200000000002, +// dns: 4.683999999999999, +// receive: 0.6780000009983667, +// send: 0.6490000000000009, +// ssl: 130.541, +// wait: 27.245000000000005, +// }); +// }); +// }); + +// describe('getSeriesAndDomain', () => { +// let seriesAndDomain: any; +// let NetworkItems: any; + +// beforeAll(() => { +// NetworkItems = extractItems(TEST_DATA); +// seriesAndDomain = getSeriesAndDomain(NetworkItems); +// }); + +// it('Correctly calculates the domain', () => { +// expect(seriesAndDomain.domain).toEqual({ max: 218.34699999913573, min: 0 }); +// }); + +// it('Correctly calculates the series', () => { +// expect(seriesAndDomain.series).toEqual([ +// { +// config: { colour: '#f3b3a6', tooltipProps: { colour: '#f3b3a6', value: '3.635ms' } }, +// x: 0, +// y: 3.6349999997764826, +// y0: 0, +// }, +// { +// config: { +// colour: '#b9a888', +// tooltipProps: { colour: '#b9a888', value: 'Queued / Blocked: 1.856ms' }, +// }, +// x: 1, +// y: 27.889999999731778, +// y0: 26.0339999999851, +// }, +// { +// config: { colour: '#54b399', tooltipProps: { colour: '#54b399', value: 'DNS: 4.684ms' } }, +// x: 1, +// y: 32.573999999731775, +// y0: 27.889999999731778, +// }, +// { +// config: { +// colour: '#da8b45', +// tooltipProps: { colour: '#da8b45', value: 'Connecting: 25.522ms' }, +// }, +// x: 1, +// y: 58.095999999731795, +// y0: 32.573999999731775, +// }, +// { +// config: { colour: '#edc5a2', tooltipProps: { colour: '#edc5a2', value: 'SSL: 130.541ms' } }, +// x: 1, +// y: 188.63699999973178, +// y0: 58.095999999731795, +// }, +// { +// config: { +// colour: '#d36086', +// tooltipProps: { colour: '#d36086', value: 'Sending request: 0.649ms' }, +// }, +// x: 1, +// y: 189.28599999973179, +// y0: 188.63699999973178, +// }, +// { +// config: { +// colour: '#b0c9e0', +// tooltipProps: { colour: '#b0c9e0', value: 'Waiting (TTFB): 27.245ms' }, +// }, +// x: 1, +// y: 216.5309999997318, +// y0: 189.28599999973179, +// }, +// { +// config: { +// colour: '#ca8eae', +// tooltipProps: { colour: '#ca8eae', value: 'Content downloading: 0.678ms' }, +// }, +// x: 1, +// y: 217.20900000073016, +// y0: 216.5309999997318, +// }, +// { +// config: { +// colour: '#b9a888', +// tooltipProps: { colour: '#b9a888', value: 'Queued / Blocked: 162.455ms' }, +// }, +// x: 2, +// y: 188.77500000020862, +// y0: 26.320000000298023, +// }, +// { +// config: { +// colour: '#d36086', +// tooltipProps: { colour: '#d36086', value: 'Sending request: 0.515ms' }, +// }, +// x: 2, +// y: 189.2900000002086, +// y0: 188.77500000020862, +// }, +// { +// config: { +// colour: '#b0c9e0', +// tooltipProps: { colour: '#b0c9e0', value: 'Waiting (TTFB): 28.494ms' }, +// }, +// x: 2, +// y: 217.7840000002086, +// y0: 189.2900000002086, +// }, +// { +// config: { +// colour: '#9170b8', +// tooltipProps: { colour: '#9170b8', value: 'Content downloading: 0.563ms' }, +// }, +// x: 2, +// y: 218.34699999913573, +// y0: 217.7840000002086, +// }, +// { +// config: { colour: '#9170b8', tooltipProps: { colour: '#9170b8', value: '12.139ms' } }, +// x: 3, +// y: 46.15699999965727, +// y0: 34.01799999922514, +// }, +// { +// config: { colour: '#9170b8', tooltipProps: { colour: '#9170b8', value: '8.453ms' } }, +// x: 4, +// y: 43.506999999284744, +// y0: 35.053999999538064, +// }, +// ]); +// }); +// }); + +describe('Palettes', () => { + it('A colour palette comprising timing and mime type colours is correctly generated', () => { + expect(colourPalette).toEqual({ + blocked: '#b9a888', + connect: '#da8b45', + dns: '#54b399', + font: '#aa6556', + html: '#f3b3a6', + media: '#d6bf57', + other: '#e7664c', + receive: '#54b399', + script: '#9170b8', + send: '#d36086', + ssl: '#edc5a2', + stylesheet: '#ca8eae', + wait: '#b0c9e0', + }); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/data_formatting.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/data_formatting.ts new file mode 100644 index 0000000000000..9c66ea638c942 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/data_formatting.ts @@ -0,0 +1,336 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { euiPaletteColorBlind } from '@elastic/eui'; + +import { + PayloadTimings, + CalculatedTimings, + NetworkItems, + FriendlyTimingLabels, + FriendlyMimetypeLabels, + MimeType, + MimeTypesMap, + Timings, + TIMING_ORDER, + SidebarItems, + LegendItems, +} from './types'; +import { WaterfallData } from '../../../waterfall'; + +const microToMillis = (micro: number): number => (micro === -1 ? -1 : micro * 1000); + +// The timing calculations here are based off several sources: +// https://github.com/ChromeDevTools/devtools-frontend/blob/2fe91adefb2921b4deb2b4b125370ef9ccdb8d1b/front_end/sdk/HARLog.js#L307 +// and +// https://chromium.googlesource.com/chromium/blink.git/+/master/Source/devtools/front_end/sdk/HAREntry.js#131 +// and +// https://github.com/cyrus-and/chrome-har-capturer/blob/master/lib/har.js#L195 +// Order of events: request_start = 0, [proxy], [dns], [connect [ssl]], [send], receive_headers_end + +export const getTimings = ( + timings: PayloadTimings, + requestSentTime: number, + responseReceivedTime: number +): CalculatedTimings => { + if (!timings) return { blocked: -1, dns: -1, connect: -1, send: 0, wait: 0, receive: 0, ssl: -1 }; + + const getLeastNonNegative = (values: number[]) => + values.reduce((best, value) => (value >= 0 && value < best ? value : best), Infinity); + const getOptionalTiming = (_timings: PayloadTimings, key: keyof PayloadTimings) => + _timings[key] >= 0 ? _timings[key] : -1; + + // NOTE: Request sent and request start can differ due to queue times + const requestStartTime = microToMillis(timings.request_time); + + // Queued + const queuedTime = requestSentTime < requestStartTime ? requestStartTime - requestSentTime : -1; + + // Blocked + // "blocked" represents both queued time + blocked/stalled time + proxy time (ie: anything before the request was actually started). + let blocked = queuedTime; + + const blockedStart = getLeastNonNegative([ + timings.dns_start, + timings.connect_start, + timings.send_start, + ]); + + if (blockedStart !== Infinity) { + blocked += blockedStart; + } + + // Proxy + // Proxy is part of blocked, but it can be quirky in that blocked can be -1 even though there are proxy timings. This can happen with + // protocols like Quic. + if (timings.proxy_end !== -1) { + const blockedProxy = timings.proxy_end - timings.proxy_start; + + if (blockedProxy && blockedProxy > blocked) { + blocked = blockedProxy; + } + } + + // DNS + const dnsStart = timings.dns_end >= 0 ? blockedStart : 0; + const dnsEnd = getOptionalTiming(timings, 'dns_end'); + const dns = dnsEnd - dnsStart; + + // SSL + const sslStart = getOptionalTiming(timings, 'ssl_start'); + const sslEnd = getOptionalTiming(timings, 'ssl_end'); + let ssl; + + if (sslStart >= 0 && sslEnd >= 0) { + ssl = timings.ssl_end - timings.ssl_start; + } + + // Connect + let connect = -1; + if (timings.connect_start >= 0) { + connect = timings.send_start - timings.connect_start; + } + + // Send + const send = timings.send_end - timings.send_start; + + // Wait + const wait = timings.receive_headers_end - timings.send_end; + + // Receive + const receive = responseReceivedTime - (requestStartTime + timings.receive_headers_end); + + // SSL connection is a part of the overall connection time + if (connect && ssl) { + connect = connect - ssl; + } + + return { blocked, dns, connect, send, wait, receive, ssl }; +}; + +// TODO: Switch to real API data, and type data as the payload response (if server response isn't preformatted) +export const extractItems = (data: any): NetworkItems => { + const items = data + .map((entry: any) => { + const requestSentTime = microToMillis(entry.synthetics.payload.start); + const responseReceivedTime = microToMillis(entry.synthetics.payload.end); + const requestStartTime = + entry.synthetics.payload.response && entry.synthetics.payload.response.timing + ? microToMillis(entry.synthetics.payload.response.timing.request_time) + : null; + + return { + timestamp: entry['@timestamp'], + method: entry.synthetics.payload.method, + url: entry.synthetics.payload.url, + status: entry.synthetics.payload.status, + mimeType: entry.synthetics.payload?.response?.mime_type, + requestSentTime, + responseReceivedTime, + earliestRequestTime: requestStartTime + ? Math.min(requestSentTime, requestStartTime) + : requestSentTime, + timings: + entry.synthetics.payload.response && entry.synthetics.payload.response.timing + ? getTimings( + entry.synthetics.payload.response.timing, + requestSentTime, + responseReceivedTime + ) + : null, + }; + }) + .sort((a: any, b: any) => { + return a.earliestRequestTime - b.earliestRequestTime; + }); + + return items; +}; + +const formatValueForDisplay = (value: number, points: number = 3) => { + return Number(value).toFixed(points); +}; + +const getColourForMimeType = (mimeType?: string) => { + const key = mimeType && MimeTypesMap[mimeType] ? MimeTypesMap[mimeType] : MimeType.Other; + return colourPalette[key]; +}; + +export const getSeriesAndDomain = (items: NetworkItems) => { + // The earliest point in time a request is sent or started. This will become our notion of "0". + const zeroOffset = items.reduce((acc, item) => { + const { earliestRequestTime } = item; + return earliestRequestTime < acc ? earliestRequestTime : acc; + }, Infinity); + + const series = items.reduce((acc, item, index) => { + const { earliestRequestTime } = item; + + // Entries without timings should be handled differently: + // https://github.com/ChromeDevTools/devtools-frontend/blob/ed2a064ac194bfae4e25c4748a9fa3513b3e9f7d/front_end/network/RequestTimingView.js#L140 + // If there are no concrete timings just plot one block via start and end + if (!item.timings || item.timings === null) { + const duration = item.responseReceivedTime - item.earliestRequestTime; + const colour = getColourForMimeType(item.mimeType); + return [ + ...acc, + { + x: index, + y0: item.earliestRequestTime - zeroOffset, + y: item.responseReceivedTime - zeroOffset, + config: { + colour, + tooltipProps: { + value: `${formatValueForDisplay(duration)}ms`, + colour, + }, + }, + }, + ]; + } + + let currentOffset = earliestRequestTime - zeroOffset; + + TIMING_ORDER.forEach((timing) => { + const value = item.timings![timing]; + const colour = + timing === Timings.Receive ? getColourForMimeType(item.mimeType) : colourPalette[timing]; + if (value && value >= 0) { + const y = currentOffset + value; + + acc.push({ + x: index, + y0: currentOffset, + y, + config: { + colour, + tooltipProps: { + value: `${FriendlyTimingLabels[timing]}: ${formatValueForDisplay( + y - currentOffset + )}ms`, + colour, + }, + }, + }); + currentOffset = y; + } + }); + return acc; + }, []); + + const yValues = series.map((serie) => serie.y); + const domain = { min: 0, max: Math.max(...yValues) }; + return { series, domain }; +}; + +export const getSidebarItems = (items: NetworkItems): SidebarItems => { + return items.map((item) => { + const { url, status, method } = item; + return { url, status, method }; + }); +}; + +export const getLegendItems = (): LegendItems => { + let timingItems: LegendItems = []; + Object.values(Timings).forEach((timing) => { + // The "receive" timing is mapped to a mime type colour, so we don't need to show this in the legend + if (timing === Timings.Receive) { + return; + } + timingItems = [ + ...timingItems, + { name: FriendlyTimingLabels[timing], colour: TIMING_PALETTE[timing] }, + ]; + }); + + let mimeTypeItems: LegendItems = []; + Object.values(MimeType).forEach((mimeType) => { + mimeTypeItems = [ + ...mimeTypeItems, + { name: FriendlyMimetypeLabels[mimeType], colour: MIME_TYPE_PALETTE[mimeType] }, + ]; + }); + return [...timingItems, ...mimeTypeItems]; +}; + +// Timing colour palette +type TimingColourPalette = { + [K in Timings]: string; +}; + +const SAFE_PALETTE = euiPaletteColorBlind({ rotations: 2 }); + +const buildTimingPalette = (): TimingColourPalette => { + const palette = Object.values(Timings).reduce>((acc, value) => { + switch (value) { + case Timings.Blocked: + acc[value] = SAFE_PALETTE[6]; + break; + case Timings.Dns: + acc[value] = SAFE_PALETTE[0]; + break; + case Timings.Connect: + acc[value] = SAFE_PALETTE[7]; + break; + case Timings.Ssl: + acc[value] = SAFE_PALETTE[17]; + break; + case Timings.Send: + acc[value] = SAFE_PALETTE[2]; + break; + case Timings.Wait: + acc[value] = SAFE_PALETTE[11]; + break; + case Timings.Receive: + acc[value] = SAFE_PALETTE[0]; + break; + } + return acc; + }, {}); + + return palette as TimingColourPalette; +}; + +const TIMING_PALETTE = buildTimingPalette(); + +// MimeType colour palette +type MimeTypeColourPalette = { + [K in MimeType]: string; +}; + +const buildMimeTypePalette = (): MimeTypeColourPalette => { + const palette = Object.values(MimeType).reduce>((acc, value) => { + switch (value) { + case MimeType.Html: + acc[value] = SAFE_PALETTE[19]; + break; + case MimeType.Script: + acc[value] = SAFE_PALETTE[3]; + break; + case MimeType.Stylesheet: + acc[value] = SAFE_PALETTE[4]; + break; + case MimeType.Media: + acc[value] = SAFE_PALETTE[5]; + break; + case MimeType.Font: + acc[value] = SAFE_PALETTE[8]; + break; + case MimeType.Other: + acc[value] = SAFE_PALETTE[9]; + break; + } + return acc; + }, {}); + + return palette as MimeTypeColourPalette; +}; + +const MIME_TYPE_PALETTE = buildMimeTypePalette(); + +type ColourPalette = TimingColourPalette & MimeTypeColourPalette; + +export const colourPalette: ColourPalette = { ...TIMING_PALETTE, ...MIME_TYPE_PALETTE }; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/types.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/types.ts new file mode 100644 index 0000000000000..1dd58b4f86db3 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/types.ts @@ -0,0 +1,197 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export enum Timings { + Blocked = 'blocked', + Dns = 'dns', + Connect = 'connect', + Ssl = 'ssl', + Send = 'send', + Wait = 'wait', + Receive = 'receive', +} + +export const FriendlyTimingLabels = { + [Timings.Blocked]: i18n.translate( + 'xpack.uptime.synthetics.waterfallChart.labels.timings.blocked', + { + defaultMessage: 'Queued / Blocked', + } + ), + [Timings.Dns]: i18n.translate('xpack.uptime.synthetics.waterfallChart.labels.timings.dns', { + defaultMessage: 'DNS', + }), + [Timings.Connect]: i18n.translate( + 'xpack.uptime.synthetics.waterfallChart.labels.timings.connect', + { + defaultMessage: 'Connecting', + } + ), + [Timings.Ssl]: i18n.translate('xpack.uptime.synthetics.waterfallChart.labels.timings.ssl', { + defaultMessage: 'SSL', + }), + [Timings.Send]: i18n.translate('xpack.uptime.synthetics.waterfallChart.labels.timings.send', { + defaultMessage: 'Sending request', + }), + [Timings.Wait]: i18n.translate('xpack.uptime.synthetics.waterfallChart.labels.timings.wait', { + defaultMessage: 'Waiting (TTFB)', + }), + [Timings.Receive]: i18n.translate( + 'xpack.uptime.synthetics.waterfallChart.labels.timings.receive', + { + defaultMessage: 'Content downloading', + } + ), +}; + +export const TIMING_ORDER = [ + Timings.Blocked, + Timings.Dns, + Timings.Connect, + Timings.Ssl, + Timings.Send, + Timings.Wait, + Timings.Receive, +] as const; + +export type CalculatedTimings = { + [K in Timings]?: number; +}; + +export enum MimeType { + Html = 'html', + Script = 'script', + Stylesheet = 'stylesheet', + Media = 'media', + Font = 'font', + Other = 'other', +} + +export const FriendlyMimetypeLabels = { + [MimeType.Html]: i18n.translate('xpack.uptime.synthetics.waterfallChart.labels.mimeTypes.html', { + defaultMessage: 'HTML', + }), + [MimeType.Script]: i18n.translate( + 'xpack.uptime.synthetics.waterfallChart.labels.mimeTypes.script', + { + defaultMessage: 'JS', + } + ), + [MimeType.Stylesheet]: i18n.translate( + 'xpack.uptime.synthetics.waterfallChart.labels.mimeTypes.stylesheet', + { + defaultMessage: 'CSS', + } + ), + [MimeType.Media]: i18n.translate( + 'xpack.uptime.synthetics.waterfallChart.labels.mimeTypes.media', + { + defaultMessage: 'Media', + } + ), + [MimeType.Font]: i18n.translate('xpack.uptime.synthetics.waterfallChart.labels.mimeTypes.font', { + defaultMessage: 'Font', + }), + [MimeType.Other]: i18n.translate( + 'xpack.uptime.synthetics.waterfallChart.labels.mimeTypes.other', + { + defaultMessage: 'Other', + } + ), +}; + +// NOTE: This list tries to cover the standard spec compliant mime types, +// and a few popular non-standard ones, but it isn't exhaustive. +export const MimeTypesMap: Record = { + 'text/html': MimeType.Html, + 'application/javascript': MimeType.Script, + 'text/javascript': MimeType.Script, + 'text/css': MimeType.Stylesheet, + // Images + 'image/apng': MimeType.Media, + 'image/bmp': MimeType.Media, + 'image/gif': MimeType.Media, + 'image/x-icon': MimeType.Media, + 'image/jpeg': MimeType.Media, + 'image/png': MimeType.Media, + 'image/svg+xml': MimeType.Media, + 'image/tiff': MimeType.Media, + 'image/webp': MimeType.Media, + // Common audio / video formats + 'audio/wave': MimeType.Media, + 'audio/wav': MimeType.Media, + 'audio/x-wav': MimeType.Media, + 'audio/x-pn-wav': MimeType.Media, + 'audio/webm': MimeType.Media, + 'video/webm': MimeType.Media, + 'audio/ogg': MimeType.Media, + 'video/ogg': MimeType.Media, + 'application/ogg': MimeType.Media, + // Fonts + 'font/otf': MimeType.Font, + 'font/ttf': MimeType.Font, + 'font/woff': MimeType.Font, + 'font/woff2': MimeType.Font, + 'application/x-font-opentype': MimeType.Font, + 'application/font-woff': MimeType.Font, + 'application/font-woff2': MimeType.Font, + 'application/vnd.ms-fontobject': MimeType.Font, + 'application/font-sfnt': MimeType.Font, +}; + +export interface NetworkItem { + timestamp: string; + method: string; + url: string; + status: number; + mimeType?: string; + // NOTE: This is the time the request was actually issued. timing.request_time might be later if the request was queued. + requestSentTime: number; + responseReceivedTime: number; + // NOTE: Denotes the earlier figure out of request sent time and request start time (part of timings). This can vary based on queue times, and + // also whether an entry actually has timings available. + // Ref: https://github.com/ChromeDevTools/devtools-frontend/blob/ed2a064ac194bfae4e25c4748a9fa3513b3e9f7d/front_end/network/RequestTimingView.js#L154 + earliestRequestTime: number; + timings: CalculatedTimings | null; +} +export type NetworkItems = NetworkItem[]; + +// NOTE: A number will always be present if the property exists, but that number might be -1, which represents no value. +export interface PayloadTimings { + dns_start: number; + push_end: number; + worker_fetch_start: number; + worker_respond_with_settled: number; + proxy_end: number; + worker_start: number; + worker_ready: number; + send_end: number; + connect_end: number; + connect_start: number; + send_start: number; + proxy_start: number; + push_start: number; + ssl_end: number; + receive_headers_end: number; + ssl_start: number; + request_time: number; + dns_end: number; +} + +export interface ExtraSeriesConfig { + colour: string; +} + +export type SidebarItem = Pick; +export type SidebarItems = SidebarItem[]; + +export interface LegendItem { + name: string; + colour: string; +} +export type LegendItems = LegendItem[]; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/waterfall_chart_wrapper.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/waterfall_chart_wrapper.tsx new file mode 100644 index 0000000000000..434b44a94b79f --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/waterfall_chart_wrapper.tsx @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useMemo, useState } from 'react'; +import { EuiHealth, EuiFlexGroup, EuiFlexItem, EuiBadge } from '@elastic/eui'; +import { getSeriesAndDomain, getSidebarItems, getLegendItems } from './data_formatting'; +import { SidebarItem, LegendItem, NetworkItems } from './types'; +import { + WaterfallProvider, + WaterfallChart, + MiddleTruncatedText, + RenderItem, +} from '../../../waterfall'; + +const renderSidebarItem: RenderItem = (item, index) => { + const { status } = item; + + const isErrorStatusCode = (statusCode: number) => { + const is400 = statusCode >= 400 && statusCode <= 499; + const is500 = statusCode >= 500 && statusCode <= 599; + const isSpecific300 = statusCode === 301 || statusCode === 307 || statusCode === 308; + return is400 || is500 || isSpecific300; + }; + + return ( + <> + {!isErrorStatusCode(status) ? ( + + ) : ( + + + + + + {status} + + + )} + + ); +}; + +const renderLegendItem: RenderItem = (item) => { + return {item.name}; +}; + +export const WaterfallChartWrapper = () => { + // TODO: Will be sourced via an API + const [networkData] = useState([]); + + const { series, domain } = useMemo(() => { + return getSeriesAndDomain(networkData); + }, [networkData]); + + const sidebarItems = useMemo(() => { + return getSidebarItems(networkData); + }, [networkData]); + + const legendItems = getLegendItems(); + + return ( + { + return {tooltipProps.value}; + }} + > + `${Number(d).toFixed(0)} ms`} + domain={domain} + barStyleAccessor={(datum) => { + return datum.datum.config.colour; + }} + renderSidebarItem={renderSidebarItem} + renderLegendItem={renderLegendItem} + /> + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx new file mode 100644 index 0000000000000..ccee9d7994c80 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { createContext, useContext, Context } from 'react'; +import { WaterfallData, WaterfallDataEntry } from '../types'; + +export interface IWaterfallContext { + data: WaterfallData; + sidebarItems?: unknown[]; + legendItems?: unknown[]; + renderTooltipItem: ( + item: WaterfallDataEntry['config']['tooltipProps'], + index?: number + ) => JSX.Element; +} + +export const WaterfallContext = createContext>({}); + +interface ProviderProps { + data: IWaterfallContext['data']; + sidebarItems?: IWaterfallContext['sidebarItems']; + legendItems?: IWaterfallContext['legendItems']; + renderTooltipItem: IWaterfallContext['renderTooltipItem']; +} + +export const WaterfallProvider: React.FC = ({ + children, + data, + sidebarItems, + legendItems, + renderTooltipItem, +}) => { + return ( + + {children} + + ); +}; + +export const useWaterfallContext = () => + useContext((WaterfallContext as unknown) as Context); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/index.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/index.tsx new file mode 100644 index 0000000000000..c3ea39a9ace6e --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/index.tsx @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { WaterfallChart, RenderItem, WaterfallChartProps } from './components/waterfall_chart'; +export { WaterfallProvider, useWaterfallContext } from './context/waterfall_chart'; +export { MiddleTruncatedText } from './components/middle_truncated_text'; +export { WaterfallData, WaterfallDataEntry } from './types'; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/types.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/types.ts new file mode 100644 index 0000000000000..d6901fb482599 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/types.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; + * you may not use this file except in compliance with the Elastic License. + */ + +interface PlotProperties { + x: number; + y: number; + y0: number; +} + +export interface WaterfallDataSeriesConfigProperties { + tooltipProps: Record; +} + +export type WaterfallDataEntry = PlotProperties & { + config: WaterfallDataSeriesConfigProperties & Record; +}; + +export type WaterfallData = WaterfallDataEntry[]; From 90a18cc15d71212363fa9081c545c9ce19f27c56 Mon Sep 17 00:00:00 2001 From: John Schulz Date: Wed, 2 Dec 2020 09:49:21 -0500 Subject: [PATCH 04/40] [Fleet][EPM] Pass through valid manifest values from upload (#84703) * Add missing properties & improve type safety * Break up types for better readability --- .../server/services/epm/archive/validation.ts | 80 ++++++++++++++----- 1 file changed, 58 insertions(+), 22 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/epm/archive/validation.ts b/x-pack/plugins/fleet/server/services/epm/archive/validation.ts index dc7a91e08799c..b93a9119cd4df 100644 --- a/x-pack/plugins/fleet/server/services/epm/archive/validation.ts +++ b/x-pack/plugins/fleet/server/services/epm/archive/validation.ts @@ -5,7 +5,7 @@ */ import yaml from 'js-yaml'; -import { uniq } from 'lodash'; +import { pick, uniq } from 'lodash'; import { ArchivePackage, RegistryPolicyTemplate, @@ -21,6 +21,42 @@ import { pkgToPkgKey } from '../registry'; const MANIFESTS: Record = {}; const MANIFEST_NAME = 'manifest.yml'; +// not sure these are 100% correct but they do the job here +// keeping them local until others need them +type OptionalPropertyOf = Exclude< + { + [K in keyof T]: T extends Record ? never : K; + }[keyof T], + undefined +>; +type RequiredPropertyOf = Exclude>; + +type RequiredPackageProp = RequiredPropertyOf; +type OptionalPackageProp = OptionalPropertyOf; +// pro: guarantee only supplying known values. these keys must be in ArchivePackage. no typos or new values +// pro: any values added to these lists will be passed through by default +// pro & con: values do need to be shadowed / repeated from ArchivePackage, but perhaps we want to limit values +const requiredArchivePackageProps: readonly RequiredPackageProp[] = [ + 'name', + 'version', + 'description', + 'type', + 'categories', + 'format_version', +] as const; + +const optionalArchivePackageProps: readonly OptionalPackageProp[] = [ + 'title', + 'release', + 'readme', + 'screenshots', + 'icons', + 'assets', + 'internal', + 'data_streams', + 'policy_templates', +] as const; + // TODO: everything below performs verification of manifest.yml files, and hence duplicates functionality already implemented in the // package registry. At some point this should probably be replaced (or enhanced) with verification based on // https://github.com/elastic/package-spec/ @@ -58,43 +94,43 @@ function parseAndVerifyArchive(paths: string[]): ArchivePackage { } // ... which must be valid YAML - let manifest; + let manifest: ArchivePackage; try { manifest = yaml.load(manifestBuffer.toString()); } catch (error) { throw new PackageInvalidArchiveError(`Could not parse top-level package manifest: ${error}.`); } - // Package name and version from the manifest must match those from the toplevel directory - const pkgKey = pkgToPkgKey({ name: manifest.name, version: manifest.version }); - if (toplevelDir !== pkgKey) { + // must have mandatory fields + const reqGiven = pick(manifest, requiredArchivePackageProps); + const requiredKeysMatch = + Object.keys(reqGiven).toString() === requiredArchivePackageProps.toString(); + if (!requiredKeysMatch) { + const list = requiredArchivePackageProps.join(', '); throw new PackageInvalidArchiveError( - `Name ${manifest.name} and version ${manifest.version} do not match top-level directory ${toplevelDir}` + `Invalid top-level package manifest: one or more fields missing of ${list}` ); } - const { name, version, description, type, categories, format_version: formatVersion } = manifest; - // check for mandatory fields - if (!(name && version && description && type && categories && formatVersion)) { + // at least have all required properties + // get optional values and combine into one object for the remaining operations + const optGiven = pick(manifest, optionalArchivePackageProps); + const parsed: ArchivePackage = { ...reqGiven, ...optGiven }; + + // Package name and version from the manifest must match those from the toplevel directory + const pkgKey = pkgToPkgKey({ name: parsed.name, version: parsed.version }); + if (toplevelDir !== pkgKey) { throw new PackageInvalidArchiveError( - 'Invalid top-level package manifest: one or more fields missing of name, version, description, type, categories, format_version' + `Name ${parsed.name} and version ${parsed.version} do not match top-level directory ${toplevelDir}` ); } - const dataStreams = parseAndVerifyDataStreams(paths, name, version); - const policyTemplates = parseAndVerifyPolicyTemplates(manifest); + parsed.data_streams = parseAndVerifyDataStreams(paths, parsed.name, parsed.version); + parsed.policy_templates = parseAndVerifyPolicyTemplates(manifest); - return { - name, - version, - description, - type, - categories, - format_version: formatVersion, - data_streams: dataStreams, - policy_templates: policyTemplates, - }; + return parsed; } + function parseAndVerifyDataStreams( paths: string[], pkgName: string, From cc341b32354c71a3d0220328ce4f1787bba3caf0 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Wed, 2 Dec 2020 16:06:18 +0100 Subject: [PATCH 05/40] Telemetry for Dyanmic Actions (Drilldowns) (#84580) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 set up telemetry for UiActions * feat: 🎸 improve ui_actions_enhanced collector * feat: 🎸 namespace ui actions telemetry stats * refactor: 💡 improve dynamic actions collector setup * feat: 🎸 add tests for dynamicActionsCollector * feat: 🎸 collect dynamic action trigger statistics * refactor: 💡 standartize metric naming * feat: 🎸 aggregate action x trigger counts * test: 💍 add tests for factory stats * docs: ✏️ add ui actions enhanced telemetry docs * fix: 🐛 revert type change * refactor: 💡 make dynamic action stats global * refactor: 💡 use global telemetry stats in action factories --- x-pack/plugins/ui_actions_enhanced/README.md | 63 ++++ .../server/dynamic_action_enhancement.ts | 15 +- ...dynamic_action_factories_collector.test.ts | 124 ++++++++ .../dynamic_action_factories_collector.ts | 24 ++ .../dynamic_actions_collector.test.ts | 271 ++++++++++++++++++ .../telemetry/dynamic_actions_collector.ts | 36 +++ .../server/telemetry/get_metric_key.ts | 10 + 7 files changed, 536 insertions(+), 7 deletions(-) create mode 100644 x-pack/plugins/ui_actions_enhanced/server/telemetry/dynamic_action_factories_collector.test.ts create mode 100644 x-pack/plugins/ui_actions_enhanced/server/telemetry/dynamic_action_factories_collector.ts create mode 100644 x-pack/plugins/ui_actions_enhanced/server/telemetry/dynamic_actions_collector.test.ts create mode 100644 x-pack/plugins/ui_actions_enhanced/server/telemetry/dynamic_actions_collector.ts create mode 100644 x-pack/plugins/ui_actions_enhanced/server/telemetry/get_metric_key.ts diff --git a/x-pack/plugins/ui_actions_enhanced/README.md b/x-pack/plugins/ui_actions_enhanced/README.md index a4a37b559ff8d..cd2a34a2f7536 100644 --- a/x-pack/plugins/ui_actions_enhanced/README.md +++ b/x-pack/plugins/ui_actions_enhanced/README.md @@ -3,3 +3,66 @@ Registers commercially licensed generic actions like per panel time range and contains some code that supports drilldown work. - [__Dashboard drilldown user docs__](https://www.elastic.co/guide/en/kibana/master/drilldowns.html) + +## Dynamic Actions Telemetry + +Dynamic actions (drilldowns) report telemetry. Below is the summary of dynamic action metrics that are reported using telemetry. + +### Dynamic action count + +Total count of dynamic actions (drilldowns) on a saved object. + +``` +dynamicActions.count +``` + +### Count by factory ID + +Count of active dynamic actions (drilldowns) on a saved object by factory ID (drilldown type). + +``` +dynamicActions.actions..count +``` + +For example: + +``` +dynamicActions.actions.DASHBOARD_TO_DASHBOARD_DRILLDOWN.count +dynamicActions.actions.URL_DRILLDOWN.count +``` + +### Count by trigger + +Count of active dynamic actions (drilldowns) on a saved object by a trigger to which they are attached. + +``` +dynamicActions.triggers..count +``` + +For example: + +``` +dynamicActions.triggers.VALUE_CLICK_TRIGGER.count +dynamicActions.triggers.RANGE_SELECT_TRIGGER.count +``` + +### Count by factory and trigger + +Count of active dynamic actions (drilldowns) on a saved object by a factory ID and trigger ID. + +``` +dynamicActions.action_triggers._.count +``` + +For example: + +``` +dynamicActions.action_triggers.DASHBOARD_TO_DASHBOARD_DRILLDOWN_VALUE_CLICK_TRIGGER.count +dynamicActions.action_triggers.DASHBOARD_TO_DASHBOARD_DRILLDOWN_RANGE_SELECT_TRIGGER.count +dynamicActions.action_triggers.URL_DRILLDOWN_VALUE_CLICK_TRIGGER.count +``` + +### Factory metrics + +Each dynamic action factory (drilldown type) can report its own stats, which is +done using the `.telemetry()` method on dynamic action factories. diff --git a/x-pack/plugins/ui_actions_enhanced/server/dynamic_action_enhancement.ts b/x-pack/plugins/ui_actions_enhanced/server/dynamic_action_enhancement.ts index 4cea7ddf4854a..16e7e7967838d 100644 --- a/x-pack/plugins/ui_actions_enhanced/server/dynamic_action_enhancement.ts +++ b/x-pack/plugins/ui_actions_enhanced/server/dynamic_action_enhancement.ts @@ -8,19 +8,20 @@ import { EnhancementRegistryDefinition } from '../../../../src/plugins/embeddabl import { SavedObjectReference } from '../../../../src/core/types'; import { ActionFactory, DynamicActionsState, SerializedEvent } from './types'; import { SerializableState } from '../../../../src/plugins/kibana_utils/common'; +import { dynamicActionsCollector } from './telemetry/dynamic_actions_collector'; +import { dynamicActionFactoriesCollector } from './telemetry/dynamic_action_factories_collector'; export const dynamicActionEnhancement = ( getActionFactory: (id: string) => undefined | ActionFactory ): EnhancementRegistryDefinition => { return { id: 'dynamicActions', - telemetry: (state: SerializableState, telemetry: Record) => { - let telemetryData = telemetry; - (state as DynamicActionsState).events.forEach((event: SerializedEvent) => { - const factory = getActionFactory(event.action.factoryId); - if (factory) telemetryData = factory.telemetry(event, telemetryData); - }); - return telemetryData; + telemetry: (serializableState: SerializableState, stats: Record) => { + const state = serializableState as DynamicActionsState; + stats = dynamicActionsCollector(state, stats); + stats = dynamicActionFactoriesCollector(getActionFactory, state, stats); + + return stats; }, extract: (state: SerializableState) => { const references: SavedObjectReference[] = []; diff --git a/x-pack/plugins/ui_actions_enhanced/server/telemetry/dynamic_action_factories_collector.test.ts b/x-pack/plugins/ui_actions_enhanced/server/telemetry/dynamic_action_factories_collector.test.ts new file mode 100644 index 0000000000000..9d38fd9d302a4 --- /dev/null +++ b/x-pack/plugins/ui_actions_enhanced/server/telemetry/dynamic_action_factories_collector.test.ts @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import { dynamicActionFactoriesCollector } from './dynamic_action_factories_collector'; +import { DynamicActionsState } from '../../common'; +import { ActionFactory } from '../types'; + +type GetActionFactory = (id: string) => undefined | ActionFactory; + +const factories: Record = { + FACTORY_ID_1: ({ + id: 'FACTORY_ID_1', + telemetry: jest.fn((state: DynamicActionsState, stats: Record) => { + stats.myStat_1 = 1; + stats.myStat_2 = 123; + return stats; + }), + } as unknown) as ActionFactory, + FACTORY_ID_2: ({ + id: 'FACTORY_ID_2', + telemetry: jest.fn((state: DynamicActionsState, stats: Record) => stats), + } as unknown) as ActionFactory, + FACTORY_ID_3: ({ + id: 'FACTORY_ID_3', + telemetry: jest.fn((state: DynamicActionsState, stats: Record) => { + stats.myStat_1 = 2; + stats.stringStat = 'abc'; + return stats; + }), + } as unknown) as ActionFactory, +}; + +const getActionFactory: GetActionFactory = (id: string) => factories[id]; + +const state: DynamicActionsState = { + events: [ + { + eventId: 'eventId-1', + triggers: ['TRIGGER_1'], + action: { + factoryId: 'FACTORY_ID_1', + name: 'Click me!', + config: {}, + }, + }, + { + eventId: 'eventId-2', + triggers: ['TRIGGER_2', 'TRIGGER_3'], + action: { + factoryId: 'FACTORY_ID_2', + name: 'Click me, too!', + config: { + doCleanup: true, + }, + }, + }, + { + eventId: 'eventId-3', + triggers: ['TRIGGER_4', 'TRIGGER_1'], + action: { + factoryId: 'FACTORY_ID_3', + name: 'Go to documentation', + config: { + url: 'http://google.com', + iamFeelingLucky: true, + }, + }, + }, + ], +}; + +beforeEach(() => { + Object.values(factories).forEach((factory) => { + ((factory.telemetry as unknown) as jest.SpyInstance).mockClear(); + }); +}); + +describe('dynamicActionFactoriesCollector', () => { + test('returns empty stats when there are not dynamic actions', () => { + const stats = dynamicActionFactoriesCollector( + getActionFactory, + { + events: [], + }, + {} + ); + + expect(stats).toEqual({}); + }); + + test('calls .telemetry() method of a supplied factory', () => { + const currentState = { + events: [state.events[0]], + }; + dynamicActionFactoriesCollector(getActionFactory, currentState, {}); + + const spy1 = (factories.FACTORY_ID_1.telemetry as unknown) as jest.SpyInstance; + const spy2 = (factories.FACTORY_ID_2.telemetry as unknown) as jest.SpyInstance; + + expect(spy1).toHaveBeenCalledTimes(1); + expect(spy2).toHaveBeenCalledTimes(0); + + expect(spy1.mock.calls[0][0]).toEqual(currentState.events[0]); + expect(typeof spy1.mock.calls[0][1]).toBe('object'); + expect(!!spy1.mock.calls[0][1]).toBe(true); + }); + + test('returns stats received from factory', () => { + const currentState = { + events: [state.events[0]], + }; + const stats = dynamicActionFactoriesCollector(getActionFactory, currentState, {}); + + expect(stats).toEqual({ + myStat_1: 1, + myStat_2: 123, + }); + }); +}); diff --git a/x-pack/plugins/ui_actions_enhanced/server/telemetry/dynamic_action_factories_collector.ts b/x-pack/plugins/ui_actions_enhanced/server/telemetry/dynamic_action_factories_collector.ts new file mode 100644 index 0000000000000..2ece6102c27a4 --- /dev/null +++ b/x-pack/plugins/ui_actions_enhanced/server/telemetry/dynamic_action_factories_collector.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { DynamicActionsState } from '../../common'; +import { ActionFactory } from '../types'; + +export const dynamicActionFactoriesCollector = ( + getActionFactory: (id: string) => undefined | ActionFactory, + state: DynamicActionsState, + stats: Record +): Record => { + for (const event of state.events) { + const factory = getActionFactory(event.action.factoryId); + + if (factory) { + stats = factory.telemetry(event, stats); + } + } + + return stats; +}; diff --git a/x-pack/plugins/ui_actions_enhanced/server/telemetry/dynamic_actions_collector.test.ts b/x-pack/plugins/ui_actions_enhanced/server/telemetry/dynamic_actions_collector.test.ts new file mode 100644 index 0000000000000..99217cd98fa01 --- /dev/null +++ b/x-pack/plugins/ui_actions_enhanced/server/telemetry/dynamic_actions_collector.test.ts @@ -0,0 +1,271 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import { dynamicActionsCollector } from './dynamic_actions_collector'; +import { DynamicActionsState } from '../../common'; + +const state: DynamicActionsState = { + events: [ + { + eventId: 'eventId-1', + triggers: ['TRIGGER_1'], + action: { + factoryId: 'FACTORY_ID_1', + name: 'Click me!', + config: {}, + }, + }, + { + eventId: 'eventId-2', + triggers: ['TRIGGER_2', 'TRIGGER_3'], + action: { + factoryId: 'FACTORY_ID_2', + name: 'Click me, too!', + config: { + doCleanup: true, + }, + }, + }, + { + eventId: 'eventId-3', + triggers: ['TRIGGER_4', 'TRIGGER_1'], + action: { + factoryId: 'FACTORY_ID_1', + name: 'Go to documentation', + config: { + url: 'http://google.com', + iamFeelingLucky: true, + }, + }, + }, + ], +}; + +describe('dynamicActionsCollector', () => { + describe('dynamic action count', () => { + test('equal to zero when there are no dynamic actions', () => { + const stats = dynamicActionsCollector( + { + events: [], + }, + {} + ); + + expect(stats).toMatchObject({ + 'dynamicActions.count': 0, + }); + }); + + test('does not update existing count if there are no dynamic actions', () => { + const stats = dynamicActionsCollector( + { + events: [], + }, + { + 'dynamicActions.count': 25, + } + ); + + expect(stats).toMatchObject({ + 'dynamicActions.count': 25, + }); + }); + + test('equal to one when there is one dynamic action', () => { + const stats = dynamicActionsCollector( + { + events: [state.events[0]], + }, + {} + ); + + expect(stats).toMatchObject({ + 'dynamicActions.count': 1, + }); + }); + + test('adds one to the current dynamic action count', () => { + const stats = dynamicActionsCollector( + { + events: [state.events[0]], + }, + { + 'dynamicActions.count': 2, + } + ); + + expect(stats).toMatchObject({ + 'dynamicActions.count': 3, + }); + }); + + test('equal to three when there are three dynamic action', () => { + const stats = dynamicActionsCollector( + { + events: [state.events[0], state.events[1], state.events[2]], + }, + {} + ); + + expect(stats).toMatchObject({ + 'dynamicActions.count': 3, + }); + }); + }); + + describe('registered action counts', () => { + test('for single action sets count to one', () => { + const stats = dynamicActionsCollector( + { + events: [state.events[0]], + }, + {} + ); + + expect(stats).toMatchObject({ + 'dynamicActions.actions.FACTORY_ID_1.count': 1, + }); + }); + + test('adds count to existing action counts', () => { + const stats = dynamicActionsCollector( + { + events: [state.events[0]], + }, + { + 'dynamicActions.actions.FACTORY_ID_1.count': 5, + 'dynamicActions.actions.FACTORY_ID_2.count': 1, + } + ); + + expect(stats).toMatchObject({ + 'dynamicActions.actions.FACTORY_ID_1.count': 6, + 'dynamicActions.actions.FACTORY_ID_2.count': 1, + }); + }); + + test('aggregates count factory count', () => { + const stats = dynamicActionsCollector( + { + events: [state.events[0], state.events[2]], + }, + {} + ); + + expect(stats).toMatchObject({ + 'dynamicActions.actions.FACTORY_ID_1.count': 2, + }); + }); + + test('returns counts for every factory type', () => { + const stats = dynamicActionsCollector( + { + events: [state.events[0], state.events[2], state.events[1]], + }, + {} + ); + + expect(stats).toMatchObject({ + 'dynamicActions.actions.FACTORY_ID_1.count': 2, + 'dynamicActions.actions.FACTORY_ID_2.count': 1, + }); + }); + }); + + describe('action trigger counts', () => { + test('for single action sets count to one', () => { + const stats = dynamicActionsCollector( + { + events: [state.events[0]], + }, + {} + ); + + expect(stats).toMatchObject({ + 'dynamicActions.triggers.TRIGGER_1.count': 1, + }); + }); + + test('adds count to existing stats', () => { + const stats = dynamicActionsCollector( + { + events: [state.events[0]], + }, + { + 'dynamicActions.triggers.TRIGGER_1.count': 123, + } + ); + + expect(stats).toMatchObject({ + 'dynamicActions.triggers.TRIGGER_1.count': 124, + }); + }); + + test('aggregates trigger counts from all dynamic actions', () => { + const stats = dynamicActionsCollector( + { + events: [state.events[0], state.events[2], state.events[1]], + }, + {} + ); + + expect(stats).toMatchObject({ + 'dynamicActions.triggers.TRIGGER_1.count': 2, + 'dynamicActions.triggers.TRIGGER_2.count': 1, + 'dynamicActions.triggers.TRIGGER_3.count': 1, + 'dynamicActions.triggers.TRIGGER_4.count': 1, + }); + }); + }); + + describe('action x trigger counts', () => { + test('returns single action (factoryId x trigger) stat', () => { + const stats = dynamicActionsCollector( + { + events: [state.events[0]], + }, + {} + ); + + expect(stats).toMatchObject({ + 'dynamicActions.action_triggers.FACTORY_ID_1_TRIGGER_1.count': 1, + }); + }); + + test('adds count to existing stats', () => { + const stats = dynamicActionsCollector( + { + events: [state.events[0]], + }, + { + 'dynamicActions.action_triggers.FACTORY_ID_1_TRIGGER_1.count': 3, + } + ); + + expect(stats).toMatchObject({ + 'dynamicActions.action_triggers.FACTORY_ID_1_TRIGGER_1.count': 4, + }); + }); + + test('aggregates actions x triggers counts for all events', () => { + const stats = dynamicActionsCollector( + { + events: [state.events[0], state.events[2], state.events[1]], + }, + {} + ); + + expect(stats).toMatchObject({ + 'dynamicActions.action_triggers.FACTORY_ID_1_TRIGGER_1.count': 2, + 'dynamicActions.action_triggers.FACTORY_ID_2_TRIGGER_2.count': 1, + 'dynamicActions.action_triggers.FACTORY_ID_2_TRIGGER_3.count': 1, + 'dynamicActions.action_triggers.FACTORY_ID_1_TRIGGER_4.count': 1, + }); + }); + }); +}); diff --git a/x-pack/plugins/ui_actions_enhanced/server/telemetry/dynamic_actions_collector.ts b/x-pack/plugins/ui_actions_enhanced/server/telemetry/dynamic_actions_collector.ts new file mode 100644 index 0000000000000..ae595776fda58 --- /dev/null +++ b/x-pack/plugins/ui_actions_enhanced/server/telemetry/dynamic_actions_collector.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { DynamicActionsState } from '../../common'; +import { getMetricKey } from './get_metric_key'; + +export const dynamicActionsCollector = ( + state: DynamicActionsState, + stats: Record +): Record => { + const countMetricKey = getMetricKey('count'); + + stats[countMetricKey] = state.events.length + (stats[countMetricKey] || 0); + + for (const event of state.events) { + const factoryId = event.action.factoryId; + const actionCountMetric = getMetricKey(`actions.${factoryId}.count`); + + stats[actionCountMetric] = 1 + (stats[actionCountMetric] || 0); + + for (const trigger of event.triggers) { + const triggerCountMetric = getMetricKey(`triggers.${trigger}.count`); + const actionXTriggerCountMetric = getMetricKey( + `action_triggers.${factoryId}_${trigger}.count` + ); + + stats[triggerCountMetric] = 1 + (stats[triggerCountMetric] || 0); + stats[actionXTriggerCountMetric] = 1 + (stats[actionXTriggerCountMetric] || 0); + } + } + + return stats; +}; diff --git a/x-pack/plugins/ui_actions_enhanced/server/telemetry/get_metric_key.ts b/x-pack/plugins/ui_actions_enhanced/server/telemetry/get_metric_key.ts new file mode 100644 index 0000000000000..6d3ae370c5200 --- /dev/null +++ b/x-pack/plugins/ui_actions_enhanced/server/telemetry/get_metric_key.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +const prefix = 'dynamicActions.'; + +/** Returns prefixed telemetry metric key for all dynamic action metrics. */ +export const getMetricKey = (path: string) => `${prefix}${path}`; From d481adc75f29b42a9abd738cf6c7f5c570d2688e Mon Sep 17 00:00:00 2001 From: Bhavya RM Date: Wed, 2 Dec 2020 10:30:27 -0500 Subject: [PATCH 06/40] Test user assignment to embeddable maps tests (#84383) --- .../apps/maps/embeddable/dashboard.js | 11 +++++++++++ .../apps/maps/embeddable/embeddable_state.js | 7 +++++++ .../apps/maps/embeddable/save_and_return.js | 17 +++++++++++++++++ .../maps/embeddable/tooltip_filter_actions.js | 16 ++++++++++++++++ 4 files changed, 51 insertions(+) diff --git a/x-pack/test/functional/apps/maps/embeddable/dashboard.js b/x-pack/test/functional/apps/maps/embeddable/dashboard.js index 0c8a208e92ece..c5c02135ea976 100644 --- a/x-pack/test/functional/apps/maps/embeddable/dashboard.js +++ b/x-pack/test/functional/apps/maps/embeddable/dashboard.js @@ -16,9 +16,19 @@ export default function ({ getPageObjects, getService }) { const testSubjects = getService('testSubjects'); const browser = getService('browser'); const retry = getService('retry'); + const security = getService('security'); describe('embed in dashboard', () => { before(async () => { + await security.testUser.setRoles( + [ + 'test_logstash_reader', + 'geoshape_data_reader', + 'meta_for_geoshape_data_reader', + 'global_dashboard_read', + ], + false + ); await kibanaServer.uiSettings.replace({ defaultIndex: 'c698b940-e149-11e8-a35a-370a8516603a', [UI_SETTINGS.COURIER_IGNORE_FILTER_IF_FIELD_NOT_IN_INDEX]: true, @@ -31,6 +41,7 @@ export default function ({ getPageObjects, getService }) { await kibanaServer.uiSettings.replace({ [UI_SETTINGS.COURIER_IGNORE_FILTER_IF_FIELD_NOT_IN_INDEX]: false, }); + await security.testUser.restoreDefaults(); }); async function getRequestTimestamp() { diff --git a/x-pack/test/functional/apps/maps/embeddable/embeddable_state.js b/x-pack/test/functional/apps/maps/embeddable/embeddable_state.js index b5640eb4ec2ea..697f6cc251b13 100644 --- a/x-pack/test/functional/apps/maps/embeddable/embeddable_state.js +++ b/x-pack/test/functional/apps/maps/embeddable/embeddable_state.js @@ -9,11 +9,14 @@ import expect from '@kbn/expect'; export default function ({ getPageObjects, getService }) { const PageObjects = getPageObjects(['common', 'dashboard', 'maps']); const kibanaServer = getService('kibanaServer'); + const security = getService('security'); const dashboardAddPanel = getService('dashboardAddPanel'); const DASHBOARD_NAME = 'verify_map_embeddable_state'; describe('embeddable state', () => { before(async () => { + await security.testUser.setRoles(['test_logstash_reader', 'global_dashboard_all']); + await kibanaServer.uiSettings.replace({ defaultIndex: 'c698b940-e149-11e8-a35a-370a8516603a', }); @@ -26,6 +29,10 @@ export default function ({ getPageObjects, getService }) { await PageObjects.dashboard.loadSavedDashboard(DASHBOARD_NAME); }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + it('should render map with center and zoom from embeddable state', async () => { const { lat, lon, zoom } = await PageObjects.maps.getView(); expect(Math.round(lat)).to.equal(0); diff --git a/x-pack/test/functional/apps/maps/embeddable/save_and_return.js b/x-pack/test/functional/apps/maps/embeddable/save_and_return.js index 4aa44799db1f4..40af8ddb9d44b 100644 --- a/x-pack/test/functional/apps/maps/embeddable/save_and_return.js +++ b/x-pack/test/functional/apps/maps/embeddable/save_and_return.js @@ -12,8 +12,25 @@ export default function ({ getPageObjects, getService }) { const dashboardPanelActions = getService('dashboardPanelActions'); const dashboardVisualizations = getService('dashboardVisualizations'); const testSubjects = getService('testSubjects'); + const security = getService('security'); describe('save and return work flow', () => { + before(async () => { + await security.testUser.setRoles( + [ + 'test_logstash_reader', + 'global_maps_all', + 'geoshape_data_reader', + 'global_dashboard_all', + 'meta_for_geoshape_data_reader', + ], + false + ); + }); + + after(async () => { + await security.testUser.restoreDefaults(); + }); describe('new map', () => { beforeEach(async () => { await PageObjects.common.navigateToApp('dashboard'); diff --git a/x-pack/test/functional/apps/maps/embeddable/tooltip_filter_actions.js b/x-pack/test/functional/apps/maps/embeddable/tooltip_filter_actions.js index d612a3776d211..f66104fc6a175 100644 --- a/x-pack/test/functional/apps/maps/embeddable/tooltip_filter_actions.js +++ b/x-pack/test/functional/apps/maps/embeddable/tooltip_filter_actions.js @@ -11,12 +11,24 @@ export default function ({ getPageObjects, getService }) { const kibanaServer = getService('kibanaServer'); const testSubjects = getService('testSubjects'); const filterBar = getService('filterBar'); + const security = getService('security'); describe('tooltip filter actions', () => { + before(async () => { + await security.testUser.setRoles([ + 'test_logstash_reader', + 'global_maps_all', + 'geoshape_data_reader', + 'global_dashboard_all', + 'meta_for_geoshape_data_reader', + 'global_discover_read', + ]); + }); async function loadDashboardAndOpenTooltip() { await kibanaServer.uiSettings.replace({ defaultIndex: 'c698b940-e149-11e8-a35a-370a8516603a', }); + await PageObjects.common.navigateToApp('dashboard'); await PageObjects.dashboard.preserveCrossAppState(); await PageObjects.dashboard.loadSavedDashboard('dash for tooltip filter action test'); @@ -24,6 +36,10 @@ export default function ({ getPageObjects, getService }) { await PageObjects.maps.lockTooltipAtPosition(200, -200); } + after(async () => { + await security.testUser.restoreDefaults(); + }); + describe('apply filter to current view', () => { before(async () => { await loadDashboardAndOpenTooltip(); From a38a9d3e0389fd7e79b8451876d1da51af360cb1 Mon Sep 17 00:00:00 2001 From: Kaarina Tungseth Date: Wed, 2 Dec 2020 09:39:26 -0600 Subject: [PATCH 07/40] [DOCS] Fix Creaxes Logstash pipeline API page (#84780) --- .../logstash-configuration-management/create-logstash.asciidoc | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/api/logstash-configuration-management/create-logstash.asciidoc b/docs/api/logstash-configuration-management/create-logstash.asciidoc index b608f4ee698f7..9bd5a9028ee9a 100644 --- a/docs/api/logstash-configuration-management/create-logstash.asciidoc +++ b/docs/api/logstash-configuration-management/create-logstash.asciidoc @@ -20,9 +20,6 @@ experimental[] Create a centrally-managed Logstash pipeline, or update an existi [[logstash-configuration-management-api-create-request-body]] ==== Request body -`id`:: - (Required, string) The pipeline ID. - `description`:: (Optional, string) The pipeline description. From c73de2677337e4054954c068744f8b29ab3ce29c Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Wed, 2 Dec 2020 11:16:59 -0500 Subject: [PATCH 08/40] [Lens] Refactor function selection invalid state (#84599) * [Lens] Refactor function selection invalid state * Fix types per review comment --- .../editor_frame/config_panel/layer_panel.tsx | 16 +- .../dimension_panel/dimension_editor.tsx | 175 +++++++++--------- .../dimension_panel/dimension_panel.test.tsx | 68 +++++-- .../dimension_panel/droppable.test.ts | 2 + .../dimension_panel/field_select.tsx | 71 +++---- .../indexpattern.test.ts | 40 ++++ .../indexpattern_datasource/indexpattern.tsx | 16 +- .../operations/__mocks__/index.ts | 1 + .../operations/definitions/column_types.ts | 3 +- .../operations/layer_helpers.test.ts | 46 +++-- .../operations/layer_helpers.ts | 109 +++++++++-- x-pack/plugins/lens/public/types.ts | 1 + 12 files changed, 376 insertions(+), 172 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 67c6068dd4d91..cc456e843bb68 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -332,7 +332,7 @@ export function LayerPanel( > setActiveDimension(initialActiveDimensionState)} + handleClose={() => { + if (layerDatasource.updateStateOnCloseDimension) { + const newState = layerDatasource.updateStateOnCloseDimension({ + state: layerDatasourceState, + layerId, + columnId: activeId!, + }); + props.updateDatasource(datasourceId, newState); + } + setActiveDimension(initialActiveDimensionState); + }} panel={ <> {activeGroup && activeId && ( (null); const selectedOperationDefinition = selectedColumn && operationDefinitionMap[selectedColumn.operationType]; + const incompleteInfo = (state.layers[layerId].incompleteColumns ?? {})[columnId]; + const incompleteOperation = incompleteInfo?.operationType; + const incompleteField = incompleteInfo?.sourceField ?? null; + const ParamEditor = selectedOperationDefinition?.paramEditor; const possibleOperations = useMemo(() => { @@ -138,7 +140,7 @@ export function DimensionEditor(props: DimensionEditorProps) { hasField(selectedColumn) && definition.input === 'field' && fieldByOperation[operationType]?.has(selectedColumn.sourceField)) || - (selectedColumn && !hasField(selectedColumn) && definition.input !== 'field'), + (selectedColumn && !hasField(selectedColumn) && definition.input === 'none'), }; }); @@ -154,10 +156,8 @@ export function DimensionEditor(props: DimensionEditorProps) { const sideNavItems: EuiListGroupItemProps[] = operationsWithCompatibility.map( ({ operationType, compatibleWithCurrentField }) => { const isActive = Boolean( - incompatibleSelectedOperationType === operationType || - (!incompatibleSelectedOperationType && - selectedColumn && - selectedColumn.operationType === operationType) + incompleteOperation === operationType || + (!incompleteOperation && selectedColumn && selectedColumn.operationType === operationType) ); let color: EuiListGroupItemProps['color'] = 'primary'; @@ -184,9 +184,17 @@ export function DimensionEditor(props: DimensionEditorProps) { }`, onClick() { if (operationDefinitionMap[operationType].input === 'none') { - // Clear invalid state because we are creating a valid column - setInvalidOperationType(null); if (selectedColumn?.operationType === operationType) { + // Clear invalid state because we are reseting to a valid column + if (incompleteInfo) { + setState( + mergeLayer({ + state, + layerId, + newLayer: resetIncomplete(state.layers[layerId], columnId), + }) + ); + } return; } const newLayer = insertOrReplaceColumn({ @@ -216,15 +224,34 @@ export function DimensionEditor(props: DimensionEditorProps) { }) ); } else { - setInvalidOperationType(operationType); + setState( + mergeLayer({ + state, + layerId, + newLayer: insertOrReplaceColumn({ + layer: props.state.layers[props.layerId], + indexPattern: currentIndexPattern, + columnId, + op: operationType, + field: undefined, + }), + }) + ); } trackUiEvent(`indexpattern_dimension_operation_${operationType}`); return; } - setInvalidOperationType(null); - if (selectedColumn.operationType === operationType) { + if (incompleteInfo) { + setState( + mergeLayer({ + state, + layerId, + newLayer: resetIncomplete(state.layers[layerId], columnId), + }) + ); + } return; } @@ -268,18 +295,17 @@ export function DimensionEditor(props: DimensionEditorProps) {
{!selectedColumn || selectedOperationDefinition?.input === 'field' || - (incompatibleSelectedOperationType && - operationDefinitionMap[incompatibleSelectedOperationType].input === 'field') ? ( + (incompleteOperation && operationDefinitionMap[incompleteOperation].input === 'field') ? ( { setState( mergeLayer({ @@ -304,53 +336,25 @@ export function DimensionEditor(props: DimensionEditorProps) { ); }} onChoose={(choice) => { - let newLayer: IndexPatternLayer; - if ( - !incompatibleSelectedOperationType && - selectedColumn && - 'field' in choice && - choice.operationType === selectedColumn.operationType - ) { - // Replaces just the field - newLayer = replaceColumn({ - layer: state.layers[layerId], - columnId, - indexPattern: currentIndexPattern, - op: choice.operationType, - field: currentIndexPattern.getFieldByName(choice.field)!, - }); - } else { - // Finds a new operation - const compatibleOperations = - ('field' in choice && operationSupportMatrix.operationByField[choice.field]) || - new Set(); - let operation; - if (compatibleOperations.size > 0) { - operation = - incompatibleSelectedOperationType && - compatibleOperations.has(incompatibleSelectedOperationType) - ? incompatibleSelectedOperationType - : compatibleOperations.values().next().value; - } else if ('field' in choice) { - operation = choice.operationType; - } - newLayer = insertOrReplaceColumn({ - layer: state.layers[layerId], - columnId, - field: currentIndexPattern.getFieldByName(choice.field), - indexPattern: currentIndexPattern, - op: operation as OperationType, - }); - } - - setState(mergeLayer({ state, layerId, newLayer })); - setInvalidOperationType(null); + setState( + mergeLayer({ + state, + layerId, + newLayer: insertOrReplaceColumn({ + layer: state.layers[layerId], + columnId, + indexPattern: currentIndexPattern, + op: choice.operationType, + field: currentIndexPattern.getFieldByName(choice.field), + }), + }) + ); }} /> ) : null} - {!currentFieldIsInvalid && !incompatibleSelectedOperationType && selectedColumn && ( + {!currentFieldIsInvalid && !incompleteInfo && selectedColumn && ( )} - {!currentFieldIsInvalid && - !incompatibleSelectedOperationType && - selectedColumn && - ParamEditor && ( - <> - - - )} + {!currentFieldIsInvalid && !incompleteInfo && selectedColumn && ParamEditor && ( + <> + + + )}
{!currentFieldIsInvalid && (
- {!incompatibleSelectedOperationType && selectedColumn && ( + {!incompleteInfo && selectedColumn && ( { @@ -411,7 +412,7 @@ export function DimensionEditor(props: DimensionEditorProps) { /> )} - {!incompatibleSelectedOperationType && !hideGrouping && ( + {!incompleteInfo && !hideGrouping && ( { columns: { col1: { label: 'Date histogram of timestamp', + customLabel: true, dataType: 'date', isBucketed: true, @@ -153,11 +154,16 @@ describe('IndexPatternDimensionEditorPanel', () => { sourceField: 'timestamp', }, }, + incompleteColumns: {}, }, }, }; - setState = jest.fn(); + setState = jest.fn().mockImplementation((newState) => { + if (wrapper instanceof ReactWrapper) { + wrapper.setProps({ state: newState }); + } + }); defaultProps = { state, @@ -544,7 +550,7 @@ describe('IndexPatternDimensionEditorPanel', () => { }); describe('transient invalid state', () => { - it('should not set the state if selecting an operation incompatible with the current field', () => { + it('should set the state if selecting an operation incompatible with the current field', () => { wrapper = mount(); act(() => { @@ -553,7 +559,20 @@ describe('IndexPatternDimensionEditorPanel', () => { .simulate('click'); }); - expect(setState).not.toHaveBeenCalled(); + expect(setState).toHaveBeenCalledWith({ + ...state, + layers: { + first: { + ...state.layers.first, + columns: { + ...state.layers.first.columns, + }, + incompleteColumns: { + col1: { operationType: 'terms' }, + }, + }, + }, + }); }); it('should show error message in invalid state', () => { @@ -566,8 +585,6 @@ describe('IndexPatternDimensionEditorPanel', () => { expect( wrapper.find('[data-test-subj="indexPattern-field-selection-row"]').first().prop('error') ).toBeDefined(); - - expect(setState).not.toHaveBeenCalled(); }); it('should leave error state if a compatible operation is selected', () => { @@ -664,6 +681,17 @@ describe('IndexPatternDimensionEditorPanel', () => { wrapper = mount(); wrapper.find('button[data-test-subj="lns-indexPatternDimension-avg"]').simulate('click'); + expect(setState).toHaveBeenCalledWith({ + ...state, + layers: { + first: { + ...state.layers.first, + incompleteColumns: { + col2: { operationType: 'avg' }, + }, + }, + }, + }); const comboBox = wrapper .find(EuiComboBox) @@ -675,7 +703,7 @@ describe('IndexPatternDimensionEditorPanel', () => { comboBox.prop('onChange')!([options![1].options![2]]); }); - expect(setState).toHaveBeenCalledWith({ + expect(setState).toHaveBeenLastCalledWith({ ...state, layers: { first: { @@ -759,11 +787,9 @@ describe('IndexPatternDimensionEditorPanel', () => { it('should set datasource state if compatible field is selected for operation', () => { wrapper = mount(); - act(() => { - wrapper - .find('button[data-test-subj="lns-indexPatternDimension-terms incompatible"]') - .simulate('click'); - }); + wrapper + .find('button[data-test-subj="lns-indexPatternDimension-terms incompatible"]') + .simulate('click'); const comboBox = wrapper .find(EuiComboBox) @@ -774,7 +800,7 @@ describe('IndexPatternDimensionEditorPanel', () => { comboBox.prop('onChange')!([option]); }); - expect(setState).toHaveBeenCalledWith({ + expect(setState).toHaveBeenLastCalledWith({ ...state, layers: { first: { @@ -1046,6 +1072,20 @@ describe('IndexPatternDimensionEditorPanel', () => { wrapper.find('button[data-test-subj="lns-indexPatternDimension-avg"]').simulate('click'); + expect(setState).toHaveBeenCalledWith({ + ...state, + layers: { + first: { + ...state.layers.first, + incompleteColumns: { + col2: { + operationType: 'avg', + }, + }, + }, + }, + }); + const comboBox = wrapper .find(EuiComboBox) .filter('[data-test-subj="indexPattern-dimension-field"]'); @@ -1212,6 +1252,9 @@ describe('IndexPatternDimensionEditorPanel', () => { }); it('should add a column on selection of a field', () => { + // Prevents field format from being loaded + setState.mockImplementation(() => {}); + wrapper = mount(); const comboBox = wrapper @@ -1231,6 +1274,7 @@ describe('IndexPatternDimensionEditorPanel', () => { columns: { ...state.layers.first.columns, col2: expect.objectContaining({ + operationType: 'range', sourceField: 'bytes', // Other parts of this don't matter for this test }), diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.test.ts index 48240a5417108..48d8d55114115 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.test.ts @@ -106,6 +106,7 @@ describe('IndexPatternDimensionEditorPanel', () => { columns: { col1: { label: 'Date histogram of timestamp', + customLabel: true, dataType: 'date', isBucketed: true, @@ -117,6 +118,7 @@ describe('IndexPatternDimensionEditorPanel', () => { sourceField: 'timestamp', }, }, + incompleteColumns: {}, }, }, }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx index 9bc3e52822cf4..135c5dcf37db9 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx @@ -28,14 +28,14 @@ import { fieldExists } from '../pure_helpers'; export interface FieldChoice { type: 'field'; field: string; - operationType?: OperationType; + operationType: OperationType; } export interface FieldSelectProps extends EuiComboBoxProps<{}> { currentIndexPattern: IndexPattern; - incompatibleSelectedOperationType: OperationType | null; - selectedColumnOperationType?: OperationType; - selectedColumnSourceField?: string; + selectedOperationType?: OperationType; + selectedField?: string; + incompleteOperation?: OperationType; operationSupportMatrix: OperationSupportMatrix; onChoose: (choice: FieldChoice) => void; onDeleteColumn: () => void; @@ -45,9 +45,9 @@ export interface FieldSelectProps extends EuiComboBoxProps<{}> { export function FieldSelect({ currentIndexPattern, - incompatibleSelectedOperationType, - selectedColumnOperationType, - selectedColumnSourceField, + incompleteOperation, + selectedOperationType, + selectedField, operationSupportMatrix, onChoose, onDeleteColumn, @@ -59,14 +59,10 @@ export function FieldSelect({ const memoizedFieldOptions = useMemo(() => { const fields = Object.keys(operationByField).sort(); + const currentOperationType = incompleteOperation ?? selectedOperationType; + function isCompatibleWithCurrentOperation(fieldName: string) { - if (incompatibleSelectedOperationType) { - return operationByField[fieldName]!.has(incompatibleSelectedOperationType); - } - return ( - !selectedColumnOperationType || - operationByField[fieldName]!.has(selectedColumnOperationType) - ); + return !currentOperationType || operationByField[fieldName]!.has(currentOperationType); } const [specialFields, normalFields] = _.partition( @@ -81,20 +77,25 @@ export function FieldSelect({ function fieldNamesToOptions(items: string[]) { return items .filter((field) => currentIndexPattern.getFieldByName(field)?.displayName) - .map((field) => ({ - label: currentIndexPattern.getFieldByName(field)?.displayName, - value: { - type: 'field', - field, - dataType: currentIndexPattern.getFieldByName(field)?.type, - operationType: - selectedColumnOperationType && isCompatibleWithCurrentOperation(field) - ? selectedColumnOperationType - : undefined, - }, - exists: containsData(field), - compatible: isCompatibleWithCurrentOperation(field), - })) + .map((field) => { + return { + label: currentIndexPattern.getFieldByName(field)?.displayName, + value: { + type: 'field', + field, + dataType: currentIndexPattern.getFieldByName(field)?.type, + // Use the operation directly, or choose the first compatible operation. + // All fields are guaranteed to have at least one operation because they + // won't appear in the list otherwise + operationType: + currentOperationType && isCompatibleWithCurrentOperation(field) + ? currentOperationType + : operationByField[field]!.values().next().value, + }, + exists: containsData(field), + compatible: isCompatibleWithCurrentOperation(field), + }; + }) .sort((a, b) => { if (a.compatible && !b.compatible) { return -1; @@ -157,8 +158,8 @@ export function FieldSelect({ metaFieldsOptions, ].filter(Boolean); }, [ - incompatibleSelectedOperationType, - selectedColumnOperationType, + incompleteOperation, + selectedOperationType, currentIndexPattern, operationByField, existingFields, @@ -174,15 +175,15 @@ export function FieldSelect({ defaultMessage: 'Field', })} options={(memoizedFieldOptions as unknown) as EuiComboBoxOptionOption[]} - isInvalid={Boolean(incompatibleSelectedOperationType || fieldIsInvalid)} + isInvalid={Boolean(incompleteOperation || fieldIsInvalid)} selectedOptions={ - ((selectedColumnOperationType && selectedColumnSourceField + ((selectedOperationType && selectedField ? [ { label: fieldIsInvalid - ? selectedColumnSourceField - : currentIndexPattern.getFieldByName(selectedColumnSourceField)?.displayName, - value: { type: 'field', field: selectedColumnSourceField }, + ? selectedField + : currentIndexPattern.getFieldByName(selectedField)?.displayName, + value: { type: 'field', field: selectedField }, }, ] : []) as unknown) as EuiComboBoxOptionOption[] diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts index 5153a74409bee..3c1c8d5f2c006 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts @@ -988,4 +988,44 @@ describe('IndexPattern Data Source', () => { expect(getErrorMessages).toHaveBeenCalledTimes(1); }); }); + + describe('#updateStateOnCloseDimension', () => { + it('should clear the incomplete column', () => { + const state = { + indexPatternRefs: [], + existingFields: {}, + isFirstExistenceFetch: false, + indexPatterns: expectedIndexPatterns, + layers: { + first: { + indexPatternId: '1', + columnOrder: [], + columns: {}, + incompleteColumns: { + col1: { operationType: 'avg' as const }, + col2: { operationType: 'sum' as const }, + }, + }, + }, + currentIndexPatternId: '1', + }; + expect( + indexPatternDatasource.updateStateOnCloseDimension!({ + state, + layerId: 'first', + columnId: 'col1', + }) + ).toEqual({ + ...state, + layers: { + first: { + indexPatternId: '1', + columnOrder: [], + columns: {}, + incompleteColumns: { col2: { operationType: 'sum' } }, + }, + }, + }); + }); + }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index 289b6bbe3f25b..948619c6b07e5 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -46,7 +46,7 @@ import { normalizeOperationDataType, } from './utils'; import { LayerPanel } from './layerpanel'; -import { IndexPatternColumn, getErrorMessages } from './operations'; +import { IndexPatternColumn, getErrorMessages, IncompleteColumn } from './operations'; import { IndexPatternField, IndexPatternPrivateState, IndexPatternPersistedState } from './types'; import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; @@ -319,6 +319,20 @@ export function getIndexPatternDatasource({ canHandleDrop, onDrop, + // Reset the temporary invalid state when closing the editor + updateStateOnCloseDimension: ({ state, layerId, columnId }) => { + const layer = { ...state.layers[layerId] }; + const newIncomplete: Record = { + ...(state.layers[layerId].incompleteColumns || {}), + }; + delete newIncomplete[columnId]; + return mergeLayer({ + state, + layerId, + newLayer: { ...layer, incompleteColumns: newIncomplete }, + }); + }, + getPublicAPI({ state, layerId }: PublicAPIProps) { const columnLabelMap = indexPatternDatasource.uniqueLabels(state); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/__mocks__/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/__mocks__/index.ts index 385f2ab941ef2..077255b75a769 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/__mocks__/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/__mocks__/index.ts @@ -40,6 +40,7 @@ export const { isColumnTransferable, getErrorMessages, isReferenced, + resetIncomplete, } = actualHelpers; export const { adjustTimeScaleLabelSuffix, DEFAULT_TIME_SCALE } = actualTimeScaleUtils; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/column_types.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/column_types.ts index d0a0fb4b28588..de3f158cca620 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/column_types.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/column_types.ts @@ -6,6 +6,7 @@ import type { Operation } from '../../../types'; import { TimeScaleUnit } from '../../time_scale'; +import type { OperationType } from '../definitions'; export interface BaseIndexPatternColumn extends Operation { // Private @@ -39,6 +40,6 @@ export interface ReferenceBasedIndexPatternColumn // Used to store the temporary invalid state export interface IncompleteColumn { - operationType?: string; + operationType?: OperationType; sourceField?: string; } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts index 0d103a766c23a..7ffbeac39c6f5 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts @@ -228,16 +228,22 @@ describe('state_helpers', () => { ).toEqual(expect.objectContaining({ columnOrder: ['col1', 'col2', 'col3'] })); }); - it('should throw if the aggregation does not support the field', () => { - expect(() => { + it('should insert both incomplete states if the aggregation does not support the field', () => { + expect( insertNewColumn({ layer: { indexPatternId: '1', columnOrder: [], columns: {} }, columnId: 'col1', indexPattern, op: 'terms', field: indexPattern.fields[0], - }); - }).toThrow(); + }) + ).toEqual( + expect.objectContaining({ + incompleteColumns: { + col1: { operationType: 'terms', sourceField: 'timestamp' }, + }, + }) + ); }); it('should put the terms agg ahead of the date histogram', () => { @@ -531,8 +537,8 @@ describe('state_helpers', () => { }).toThrow(); }); - it('should throw if switching to a field-based operation without providing a field', () => { - expect(() => { + it('should set incompleteColumns when switching to a field-based operation without providing a field', () => { + expect( replaceColumn({ layer: { indexPatternId: '1', @@ -554,12 +560,19 @@ describe('state_helpers', () => { }, columnId: 'col1', indexPattern, - op: 'date_histogram', - }); - }).toThrow(); + op: 'terms', + }) + ).toEqual( + expect.objectContaining({ + columns: { col1: expect.objectContaining({ operationType: 'date_histogram' }) }, + incompleteColumns: { + col1: { operationType: 'terms' }, + }, + }) + ); }); - it('should carry over params from old column if the switching fields', () => { + it('should carry over params from old column if switching fields', () => { expect( replaceColumn({ layer: { @@ -592,7 +605,7 @@ describe('state_helpers', () => { ); }); - it('should transition from field-based to fieldless operation', () => { + it('should transition from field-based to fieldless operation, clearing incomplete', () => { expect( replaceColumn({ layer: { @@ -612,14 +625,20 @@ describe('state_helpers', () => { }, }, }, + incompleteColumns: { + col1: { operationType: 'terms' }, + }, }, indexPattern, columnId: 'col1', op: 'filters', - }).columns.col1 + }) ).toEqual( expect.objectContaining({ - operationType: 'filters', + columns: { + col1: expect.objectContaining({ operationType: 'filters' }), + }, + incompleteColumns: {}, }) ); }); @@ -944,6 +963,7 @@ describe('state_helpers', () => { isTransferable: jest.fn(), toExpression: jest.fn().mockReturnValue([]), getPossibleOperation: jest.fn().mockReturnValue({ dataType: 'number', isBucketed: false }), + getDefaultLabel: () => 'Test reference', }; const layer: IndexPatternLayer = { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts index 260ed180da921..b16418d44ba33 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts @@ -153,15 +153,50 @@ export function insertNewColumn({ } } - if (!field) { - throw new Error(`Invariant error: ${operationDefinition.type} operation requires field`); + const invalidFieldName = (layer.incompleteColumns ?? {})[columnId]?.sourceField; + const invalidField = invalidFieldName ? indexPattern.getFieldByName(invalidFieldName) : undefined; + + if (!field && invalidField) { + const possibleOperation = operationDefinition.getPossibleOperationForField(invalidField); + if (!possibleOperation) { + throw new Error( + `Tried to create an invalid operation ${operationDefinition.type} using previously selected field ${invalidField.name}` + ); + } + const isBucketed = Boolean(possibleOperation.isBucketed); + if (isBucketed) { + return addBucket( + layer, + operationDefinition.buildColumn({ ...baseOptions, layer, field: invalidField }), + columnId + ); + } else { + return addMetric( + layer, + operationDefinition.buildColumn({ ...baseOptions, layer, field: invalidField }), + columnId + ); + } + } else if (!field) { + // Labels don't need to be updated because it's incomplete + return { + ...layer, + incompleteColumns: { + ...(layer.incompleteColumns ?? {}), + [columnId]: { operationType: op }, + }, + }; } const possibleOperation = operationDefinition.getPossibleOperationForField(field); if (!possibleOperation) { - throw new Error( - `Tried to create an invalid operation ${operationDefinition.type} on ${field.name}` - ); + return { + ...layer, + incompleteColumns: { + ...(layer.incompleteColumns ?? {}), + [columnId]: { operationType: op, sourceField: field.name }, + }, + }; } const isBucketed = Boolean(possibleOperation.isBucketed); if (isBucketed) { @@ -208,6 +243,8 @@ export function replaceColumn({ if (isNewOperation) { let tempLayer = { ...layer }; + tempLayer = resetIncomplete(tempLayer, columnId); + if (previousDefinition.input === 'fullReference') { (previousColumn as ReferenceBasedIndexPatternColumn).references.forEach((id: string) => { tempLayer = deleteColumn({ layer: tempLayer, columnId: id }); @@ -217,8 +254,6 @@ export function replaceColumn({ if (operationDefinition.input === 'fullReference') { const referenceIds = operationDefinition.requiredReferences.map(() => generateId()); - const incompleteColumns = { ...(tempLayer.incompleteColumns || {}) }; - delete incompleteColumns[columnId]; const newColumns = { ...tempLayer.columns, [columnId]: operationDefinition.buildColumn({ @@ -232,7 +267,6 @@ export function replaceColumn({ ...tempLayer, columnOrder: getColumnOrder({ ...tempLayer, columns: newColumns }), columns: newColumns, - incompleteColumns, }; } @@ -249,7 +283,13 @@ export function replaceColumn({ } if (!field) { - throw new Error(`Invariant error: ${operationDefinition.type} operation requires field`); + return { + ...tempLayer, + incompleteColumns: { + ...(tempLayer.incompleteColumns ?? {}), + [columnId]: { operationType: op }, + }, + }; } let newColumn = operationDefinition.buildColumn({ ...baseOptions, layer: tempLayer, field }); @@ -296,7 +336,7 @@ function addBucket( column: IndexPatternColumn, addedColumnId: string ): IndexPatternLayer { - const [buckets, metrics] = separateBucketColumns(layer); + const [buckets, metrics, references] = getExistingColumnGroups(layer); const oldDateHistogramIndex = layer.columnOrder.findIndex( (columnId) => layer.columns[columnId].operationType === 'date_histogram' @@ -310,17 +350,19 @@ function addBucket( addedColumnId, ...buckets.slice(oldDateHistogramIndex, buckets.length), ...metrics, + ...references, ]; } else { // Insert the new bucket after existing buckets. Users will see the same data // they already had, with an extra level of detail. - updatedColumnOrder = [...buckets, addedColumnId, ...metrics]; + updatedColumnOrder = [...buckets, addedColumnId, ...metrics, ...references]; } - return { - ...layer, + const tempLayer = { + ...resetIncomplete(layer, addedColumnId), columns: { ...layer.columns, [addedColumnId]: column }, columnOrder: updatedColumnOrder, }; + return { ...tempLayer, columnOrder: getColumnOrder(tempLayer) }; } function addMetric( @@ -328,18 +370,15 @@ function addMetric( column: IndexPatternColumn, addedColumnId: string ): IndexPatternLayer { - return { - ...layer, + const tempLayer = { + ...resetIncomplete(layer, addedColumnId), columns: { ...layer.columns, [addedColumnId]: column, }, columnOrder: [...layer.columnOrder, addedColumnId], }; -} - -function separateBucketColumns(layer: IndexPatternLayer) { - return partition(layer.columnOrder, (columnId) => layer.columns[columnId]?.isBucketed); + return { ...tempLayer, columnOrder: getColumnOrder(tempLayer) }; } export function getMetricOperationTypes(field: IndexPatternField) { @@ -442,9 +481,24 @@ export function deleteColumn({ return { ...newLayer, columnOrder: getColumnOrder(newLayer), incompleteColumns: newIncomplete }; } +// Derives column order from column object, respects existing columnOrder +// when possible, but also allows new columns to be added to the order export function getColumnOrder(layer: IndexPatternLayer): string[] { + const entries = Object.entries(layer.columns); + entries.sort(([idA], [idB]) => { + const indexA = layer.columnOrder.indexOf(idA); + const indexB = layer.columnOrder.indexOf(idB); + if (indexA > -1 && indexB > -1) { + return indexA - indexB; + } else if (indexA > -1) { + return -1; + } else { + return 1; + } + }); + const [direct, referenceBased] = _.partition( - Object.entries(layer.columns), + entries, ([id, col]) => operationDefinitionMap[col.operationType].input !== 'fullReference' ); // If a reference has another reference as input, put it last in sort order @@ -465,6 +519,15 @@ export function getColumnOrder(layer: IndexPatternLayer): string[] { .concat(referenceBased.map(([id]) => id)); } +// Splits existing columnOrder into the three categories +function getExistingColumnGroups(layer: IndexPatternLayer): [string[], string[], string[]] { + const [direct, referenced] = partition( + layer.columnOrder, + (columnId) => layer.columns[columnId] && !('references' in layer.columns[columnId]) + ); + return [...partition(direct, (columnId) => layer.columns[columnId]?.isBucketed), referenced]; +} + /** * Returns true if the given column can be applied to the given index pattern */ @@ -601,3 +664,9 @@ function isOperationAllowedAsReference({ hasValidMetadata ); } + +export function resetIncomplete(layer: IndexPatternLayer, columnId: string): IndexPatternLayer { + const incompleteColumns = { ...(layer.incompleteColumns ?? {}) }; + delete incompleteColumns[columnId]; + return { ...layer, incompleteColumns }; +} diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 65c565087af72..2b3cd02c09d1e 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -167,6 +167,7 @@ export interface Datasource { renderLayerPanel: (domElement: Element, props: DatasourceLayerPanelProps) => void; canHandleDrop: (props: DatasourceDimensionDropProps) => boolean; onDrop: (props: DatasourceDimensionDropHandlerProps) => false | true | { deleted: string }; + updateStateOnCloseDimension?: (props: { layerId: string; columnId: string; state: T }) => T; toExpression: (state: T, layerId: string) => Ast | string | null; From d990b27ab202a9cd7a3164833ca7fe417a0a8538 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Wed, 2 Dec 2020 19:28:46 +0300 Subject: [PATCH 09/40] bump es-js version (#84770) --- package.json | 2 +- yarn.lock | 16 ++++------------ 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 7028b3093dc4f..d5c1f247d87d7 100644 --- a/package.json +++ b/package.json @@ -105,7 +105,7 @@ "@babel/core": "^7.11.6", "@babel/runtime": "^7.11.2", "@elastic/datemath": "link:packages/elastic-datemath", - "@elastic/elasticsearch": "7.10.0-rc.1", + "@elastic/elasticsearch": "7.10.0", "@elastic/ems-client": "7.11.0", "@elastic/eui": "30.2.0", "@elastic/filesaver": "1.1.2", diff --git a/yarn.lock b/yarn.lock index 8644a53e6f52f..28f032ef7122f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1404,13 +1404,12 @@ version "0.0.0" uid "" -"@elastic/elasticsearch@7.10.0-rc.1": - version "7.10.0-rc.1" - resolved "https://registry.yarnpkg.com/@elastic/elasticsearch/-/elasticsearch-7.10.0-rc.1.tgz#c23fc5cbfdb40cf2ce6f9cd796b75940e8c9dc8a" - integrity sha512-STaBlEwYbT8yT3HJ+mbO1kx+Kb7Ft7Q0xG5GxZbqbAJ7PZvgGgJWwN7jUg4oKJHbTfxV3lPvFa+PaUK2TqGuYg== +"@elastic/elasticsearch@7.10.0": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@elastic/elasticsearch/-/elasticsearch-7.10.0.tgz#da105a9c1f14146f9f2cab4e7026cb7949121b8d" + integrity sha512-vXtMAQf5/DwqeryQgRriMtnFppJNLc/R7/R0D8E+wG5/kGM5i7mg+Hi7TM4NZEuXgtzZ2a/Nf7aR0vLyrxOK/w== dependencies: debug "^4.1.1" - decompress-response "^4.2.0" hpagent "^0.1.1" ms "^2.1.1" pump "^3.0.0" @@ -11187,13 +11186,6 @@ decompress-response@^3.3.0: dependencies: mimic-response "^1.0.0" -decompress-response@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.0.tgz#805ca9d1d3cdf17a03951475ad6cdc93115cec3f" - integrity sha512-MHebOkORCgLW1ramLri5vzfR4r7HgXXrVkVr/eaPVRCtYWFUp9hNAuqsBxhpABbpqd7zY2IrjxXfTuaVrW0Z2A== - dependencies: - mimic-response "^2.0.0" - decompress-response@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-5.0.0.tgz#7849396e80e3d1eba8cb2f75ef4930f76461cb0f" From cbc61afcce023d0478784c953d092e4b8dd9a375 Mon Sep 17 00:00:00 2001 From: ymao1 Date: Wed, 2 Dec 2020 11:49:24 -0500 Subject: [PATCH 10/40] [Task Manager] Skip removed task types when claiming tasks (#84273) * Checking if task type is in registered list * Loading esArchiver data with removed task type for testing * PR fixes --- .../mark_available_tasks_as_claimed.test.ts | 16 +- .../mark_available_tasks_as_claimed.ts | 16 +- x-pack/plugins/task_manager/server/task.ts | 1 + .../task_manager/server/task_store.test.ts | 32 ++- .../plugins/task_manager/server/task_store.ts | 2 + .../server/task_type_dictionary.ts | 8 +- .../task_manager_removed_types/data.json | 30 +++ .../task_manager_removed_types/mappings.json | 225 ++++++++++++++++++ .../test_suites/task_manager/index.ts | 1 + .../task_management_removed_types.ts | 101 ++++++++ 10 files changed, 408 insertions(+), 24 deletions(-) create mode 100644 x-pack/test/functional/es_archives/task_manager_removed_types/data.json create mode 100644 x-pack/test/functional/es_archives/task_manager_removed_types/mappings.json create mode 100644 x-pack/test/plugin_api_integration/test_suites/task_manager/task_management_removed_types.ts diff --git a/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.test.ts b/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.test.ts index 8a94ae4ed82f5..8b0a8323d9452 100644 --- a/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.test.ts +++ b/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.test.ts @@ -50,6 +50,7 @@ describe('mark_available_tasks_as_claimed', () => { update: updateFieldsAndMarkAsFailed( fieldUpdates, claimTasksById || [], + definitions.getAllTypes(), Array.from(definitions).reduce((accumulator, [type, { maxAttempts }]) => { return { ...accumulator, [type]: maxAttempts || defaultMaxAttempts }; }, {}) @@ -114,12 +115,16 @@ if (doc['task.runAt'].size()!=0) { seq_no_primary_term: true, script: { source: ` - if (ctx._source.task.schedule != null || ctx._source.task.attempts < params.taskMaxAttempts[ctx._source.task.taskType] || params.claimTasksById.contains(ctx._id)) { - ctx._source.task.status = "claiming"; ${Object.keys(fieldUpdates) - .map((field) => `ctx._source.task.${field}=params.fieldUpdates.${field};`) - .join(' ')} + if (params.registeredTaskTypes.contains(ctx._source.task.taskType)) { + if (ctx._source.task.schedule != null || ctx._source.task.attempts < params.taskMaxAttempts[ctx._source.task.taskType] || params.claimTasksById.contains(ctx._id)) { + ctx._source.task.status = "claiming"; ${Object.keys(fieldUpdates) + .map((field) => `ctx._source.task.${field}=params.fieldUpdates.${field};`) + .join(' ')} + } else { + ctx._source.task.status = "failed"; + } } else { - ctx._source.task.status = "failed"; + ctx._source.task.status = "unrecognized"; } `, lang: 'painless', @@ -129,6 +134,7 @@ if (doc['task.runAt'].size()!=0) { retryAt: claimOwnershipUntil, }, claimTasksById: [], + registeredTaskTypes: ['sampleTask', 'otherTask'], taskMaxAttempts: { sampleTask: 5, otherTask: 1, diff --git a/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.ts b/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.ts index 072ec4648201a..ecd8107eef915 100644 --- a/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.ts +++ b/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.ts @@ -105,21 +105,27 @@ export const updateFieldsAndMarkAsFailed = ( [field: string]: string | number | Date; }, claimTasksById: string[], + registeredTaskTypes: string[], taskMaxAttempts: { [field: string]: number } ): ScriptClause => ({ source: ` - if (ctx._source.task.schedule != null || ctx._source.task.attempts < params.taskMaxAttempts[ctx._source.task.taskType] || params.claimTasksById.contains(ctx._id)) { - ctx._source.task.status = "claiming"; ${Object.keys(fieldUpdates) - .map((field) => `ctx._source.task.${field}=params.fieldUpdates.${field};`) - .join(' ')} + if (params.registeredTaskTypes.contains(ctx._source.task.taskType)) { + if (ctx._source.task.schedule != null || ctx._source.task.attempts < params.taskMaxAttempts[ctx._source.task.taskType] || params.claimTasksById.contains(ctx._id)) { + ctx._source.task.status = "claiming"; ${Object.keys(fieldUpdates) + .map((field) => `ctx._source.task.${field}=params.fieldUpdates.${field};`) + .join(' ')} + } else { + ctx._source.task.status = "failed"; + } } else { - ctx._source.task.status = "failed"; + ctx._source.task.status = "unrecognized"; } `, lang: 'painless', params: { fieldUpdates, claimTasksById, + registeredTaskTypes, taskMaxAttempts, }, }); diff --git a/x-pack/plugins/task_manager/server/task.ts b/x-pack/plugins/task_manager/server/task.ts index 8b7870550040f..e832a95ac3caa 100644 --- a/x-pack/plugins/task_manager/server/task.ts +++ b/x-pack/plugins/task_manager/server/task.ts @@ -166,6 +166,7 @@ export enum TaskStatus { Claiming = 'claiming', Running = 'running', Failed = 'failed', + Unrecognized = 'unrecognized', } export enum TaskLifecycleResult { diff --git a/x-pack/plugins/task_manager/server/task_store.test.ts b/x-pack/plugins/task_manager/server/task_store.test.ts index 46e55df4ee1e6..81d72c68b3a9e 100644 --- a/x-pack/plugins/task_manager/server/task_store.test.ts +++ b/x-pack/plugins/task_manager/server/task_store.test.ts @@ -578,12 +578,16 @@ if (doc['task.runAt'].size()!=0) { expect(script).toMatchObject({ source: ` - if (ctx._source.task.schedule != null || ctx._source.task.attempts < params.taskMaxAttempts[ctx._source.task.taskType] || params.claimTasksById.contains(ctx._id)) { - ctx._source.task.status = "claiming"; ${Object.keys(fieldUpdates) - .map((field) => `ctx._source.task.${field}=params.fieldUpdates.${field};`) - .join(' ')} + if (params.registeredTaskTypes.contains(ctx._source.task.taskType)) { + if (ctx._source.task.schedule != null || ctx._source.task.attempts < params.taskMaxAttempts[ctx._source.task.taskType] || params.claimTasksById.contains(ctx._id)) { + ctx._source.task.status = "claiming"; ${Object.keys(fieldUpdates) + .map((field) => `ctx._source.task.${field}=params.fieldUpdates.${field};`) + .join(' ')} + } else { + ctx._source.task.status = "failed"; + } } else { - ctx._source.task.status = "failed"; + ctx._source.task.status = "unrecognized"; } `, lang: 'painless', @@ -593,6 +597,7 @@ if (doc['task.runAt'].size()!=0) { 'task:33c6977a-ed6d-43bd-98d9-3f827f7b7cd8', 'task:a208b22c-14ec-4fb4-995f-d2ff7a3b03b8', ], + registeredTaskTypes: ['foo', 'bar'], taskMaxAttempts: { bar: customMaxAttempts, foo: maxAttempts, @@ -644,18 +649,23 @@ if (doc['task.runAt'].size()!=0) { }); expect(script).toMatchObject({ source: ` - if (ctx._source.task.schedule != null || ctx._source.task.attempts < params.taskMaxAttempts[ctx._source.task.taskType] || params.claimTasksById.contains(ctx._id)) { - ctx._source.task.status = "claiming"; ${Object.keys(fieldUpdates) - .map((field) => `ctx._source.task.${field}=params.fieldUpdates.${field};`) - .join(' ')} + if (params.registeredTaskTypes.contains(ctx._source.task.taskType)) { + if (ctx._source.task.schedule != null || ctx._source.task.attempts < params.taskMaxAttempts[ctx._source.task.taskType] || params.claimTasksById.contains(ctx._id)) { + ctx._source.task.status = "claiming"; ${Object.keys(fieldUpdates) + .map((field) => `ctx._source.task.${field}=params.fieldUpdates.${field};`) + .join(' ')} + } else { + ctx._source.task.status = "failed"; + } } else { - ctx._source.task.status = "failed"; + ctx._source.task.status = "unrecognized"; } `, lang: 'painless', params: { fieldUpdates, claimTasksById: [], + registeredTaskTypes: ['report', 'dernstraight', 'yawn'], taskMaxAttempts: { dernstraight: 2, report: 2, @@ -1218,7 +1228,7 @@ if (doc['task.runAt'].size()!=0) { describe('getLifecycle', () => { test('returns the task status if the task exists ', async () => { - expect.assertions(4); + expect.assertions(5); return Promise.all( Object.values(TaskStatus).map(async (status) => { const task = { diff --git a/x-pack/plugins/task_manager/server/task_store.ts b/x-pack/plugins/task_manager/server/task_store.ts index 04ee3529bcc0b..0d5d2431e227f 100644 --- a/x-pack/plugins/task_manager/server/task_store.ts +++ b/x-pack/plugins/task_manager/server/task_store.ts @@ -260,6 +260,7 @@ export class TaskStore { claimTasksById: OwnershipClaimingOpts['claimTasksById'], size: OwnershipClaimingOpts['size'] ): Promise { + const registeredTaskTypes = this.definitions.getAllTypes(); const taskMaxAttempts = [...this.definitions].reduce((accumulator, [type, { maxAttempts }]) => { return { ...accumulator, [type]: maxAttempts || this.maxAttempts }; }, {}); @@ -297,6 +298,7 @@ export class TaskStore { retryAt: claimOwnershipUntil, }, claimTasksById || [], + registeredTaskTypes, taskMaxAttempts ), sort, diff --git a/x-pack/plugins/task_manager/server/task_type_dictionary.ts b/x-pack/plugins/task_manager/server/task_type_dictionary.ts index cb7cda6dfa845..451b5dd7cad52 100644 --- a/x-pack/plugins/task_manager/server/task_type_dictionary.ts +++ b/x-pack/plugins/task_manager/server/task_type_dictionary.ts @@ -32,6 +32,10 @@ export class TaskTypeDictionary { return this.definitions.entries(); } + public getAllTypes() { + return [...this.definitions.keys()]; + } + public has(type: string) { return this.definitions.has(type); } @@ -44,9 +48,7 @@ export class TaskTypeDictionary { public ensureHas(type: string) { if (!this.has(type)) { throw new Error( - `Unsupported task type "${type}". Supported types are ${[...this.definitions.keys()].join( - ', ' - )}` + `Unsupported task type "${type}". Supported types are ${this.getAllTypes().join(', ')}` ); } } diff --git a/x-pack/test/functional/es_archives/task_manager_removed_types/data.json b/x-pack/test/functional/es_archives/task_manager_removed_types/data.json new file mode 100644 index 0000000000000..8594e9d567b8a --- /dev/null +++ b/x-pack/test/functional/es_archives/task_manager_removed_types/data.json @@ -0,0 +1,30 @@ +{ + "type": "doc", + "value": { + "id": "task:be7e1250-3322-11eb-94c1-db6995e83f6a", + "index": ".kibana_task_manager_1", + "source": { + "migrationVersion": { + "task": "7.6.0" + }, + "references": [ + ], + "task": { + "attempts": 0, + "params": "{\"originalParams\":{},\"superFly\":\"My middleware param!\"}", + "retryAt": "2020-11-30T15:43:39.626Z", + "runAt": "2020-11-30T15:43:08.277Z", + "scheduledAt": "2020-11-30T15:43:08.277Z", + "scope": [ + "testing" + ], + "startedAt": null, + "state": "{}", + "status": "idle", + "taskType": "sampleTaskRemovedType" + }, + "type": "task", + "updated_at": "2020-11-30T15:43:08.277Z" + } + } +} \ No newline at end of file diff --git a/x-pack/test/functional/es_archives/task_manager_removed_types/mappings.json b/x-pack/test/functional/es_archives/task_manager_removed_types/mappings.json new file mode 100644 index 0000000000000..c3a10064e905e --- /dev/null +++ b/x-pack/test/functional/es_archives/task_manager_removed_types/mappings.json @@ -0,0 +1,225 @@ +{ + "type": "index", + "value": { + "aliases": { + ".kibana": { + } + }, + "index": ".kibana_1", + "mappings": { + "_meta": { + "migrationMappingPropertyHashes": { + "action": "6e96ac5e648f57523879661ea72525b7", + "action_task_params": "a9d49f184ee89641044be0ca2950fa3a", + "alert": "0359d7fcc04da9878ee9aadbda38ba55", + "api_key_pending_invalidation": "16f515278a295f6245149ad7c5ddedb7", + "apm-indices": "9bb9b2bf1fa636ed8619cbab5ce6a1dd", + "apm-telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "app_search_telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "application_usage_daily": "43b8830d5d0df85a6823d290885fc9fd", + "application_usage_totals": "3d1b76c39bfb2cc8296b024d73854724", + "application_usage_transactional": "3d1b76c39bfb2cc8296b024d73854724", + "background-session": "721df406dbb7e35ac22e4df6c3ad2b2a", + "canvas-element": "7390014e1091044523666d97247392fc", + "canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231", + "canvas-workpad-template": "ae2673f678281e2c055d764b153e9715", + "cases": "477f214ff61acc3af26a7b7818e380c1", + "cases-comments": "8a50736330e953bca91747723a319593", + "cases-configure": "387c5f3a3bda7e0ae0dd4e106f914a69", + "cases-user-actions": "32277330ec6b721abe3b846cfd939a71", + "config": "c63748b75f39d0c54de12d12c1ccbc20", + "dashboard": "40554caf09725935e2c02e02563a2d07", + "endpoint:user-artifact": "4a11183eee21e6fbad864f7a30b39ad0", + "endpoint:user-artifact-manifest": "4b9c0e7cfaf86d82a7ee9ed68065e50d", + "enterprise_search_telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "epm-packages": "2b83397e3eaaaa8ef15e38813f3721c3", + "event_log_test": "bef808d4a9c27f204ffbda3359233931", + "exception-list": "67f055ab8c10abd7b2ebfd969b836788", + "exception-list-agnostic": "67f055ab8c10abd7b2ebfd969b836788", + "file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e", + "fleet-agent-actions": "9511b565b1cc6441a42033db3d5de8e9", + "fleet-agent-events": "e20a508b6e805189356be381dbfac8db", + "fleet-agents": "cb661e8ede2b640c42c8e5ef99db0683", + "fleet-enrollment-api-keys": "a69ef7ae661dab31561d6c6f052ef2a7", + "graph-workspace": "cd7ba1330e6682e9cc00b78850874be1", + "index-pattern": "45915a1ad866812242df474eb0479052", + "infrastructure-ui-source": "3d1b76c39bfb2cc8296b024d73854724", + "ingest-agent-policies": "8b0733cce189659593659dad8db426f0", + "ingest-outputs": "8854f34453a47e26f86a29f8f3b80b4e", + "ingest-package-policies": "c91ca97b1ff700f0fc64dc6b13d65a85", + "ingest_manager_settings": "02a03095f0e05b7a538fa801b88a217f", + "inventory-view": "3d1b76c39bfb2cc8296b024d73854724", + "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", + "lens": "52346cfec69ff7b47d5f0c12361a2797", + "lens-ui-telemetry": "509bfa5978586998e05f9e303c07a327", + "map": "4a05b35c3a3a58fbc72dd0202dc3487f", + "maps-telemetry": "5ef305b18111b77789afefbd36b66171", + "metrics-explorer-view": "3d1b76c39bfb2cc8296b024d73854724", + "migrationVersion": "4a1746014a75ade3a714e1db5763276f", + "ml-job": "3bb64c31915acf93fc724af137a0891b", + "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", + "monitoring-telemetry": "2669d5ec15e82391cf58df4294ee9c68", + "namespace": "2f4316de49999235636386fe51dc06c1", + "namespaces": "2f4316de49999235636386fe51dc06c1", + "originId": "2f4316de49999235636386fe51dc06c1", + "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", + "references": "7997cf5a56cc02bdc9c93361bde732b0", + "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", + "search": "43012c7ebc4cb57054e0a490e4b43023", + "search-telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "siem-detection-engine-rule-actions": "6569b288c169539db10cb262bf79de18", + "siem-detection-engine-rule-status": "ae783f41c6937db6b7a2ef5c93a9e9b0", + "siem-ui-timeline": "d12c5474364d737d17252acf1dc4585c", + "siem-ui-timeline-note": "8874706eedc49059d4cf0f5094559084", + "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", + "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", + "tag": "83d55da58f6530f7055415717ec06474", + "telemetry": "36a616f7026dfa617d6655df850fe16d", + "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", + "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", + "type": "2f4316de49999235636386fe51dc06c1", + "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", + "updated_at": "00da57df13e94e9d98437d13ace4bfe0", + "upgrade-assistant-reindex-operation": "215107c281839ea9b3ad5f6419819763", + "upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b", + "uptime-dynamic-settings": "3d1b76c39bfb2cc8296b024d73854724", + "url": "c7f66a0df8b1b52f17c28c4adb111105", + "visualization": "f819cf6636b75c9e76ba733a0c6ef355", + "workplace_search_telemetry": "3d1b76c39bfb2cc8296b024d73854724" + } + }, + "dynamic": "strict", + "properties": { + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "number_of_replicas": "0", + "number_of_shards": "1" + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + ".kibana_task_manager": { + } + }, + "index": ".kibana_task_manager_1", + "mappings": { + "_meta": { + "migrationMappingPropertyHashes": { + "migrationVersion": "4a1746014a75ade3a714e1db5763276f", + "namespace": "2f4316de49999235636386fe51dc06c1", + "namespaces": "2f4316de49999235636386fe51dc06c1", + "originId": "2f4316de49999235636386fe51dc06c1", + "references": "7997cf5a56cc02bdc9c93361bde732b0", + "task": "235412e52d09e7165fac8a67a43ad6b4", + "type": "2f4316de49999235636386fe51dc06c1", + "updated_at": "00da57df13e94e9d98437d13ace4bfe0" + } + }, + "dynamic": "strict", + "properties": { + "migrationVersion": { + "dynamic": "true", + "properties": { + "task": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "references": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "task": { + "properties": { + "attempts": { + "type": "integer" + }, + "ownerId": { + "type": "keyword" + }, + "params": { + "type": "text" + }, + "retryAt": { + "type": "date" + }, + "runAt": { + "type": "date" + }, + "schedule": { + "properties": { + "interval": { + "type": "keyword" + } + } + }, + "scheduledAt": { + "type": "date" + }, + "scope": { + "type": "keyword" + }, + "startedAt": { + "type": "date" + }, + "state": { + "type": "text" + }, + "status": { + "type": "keyword" + }, + "taskType": { + "type": "keyword" + }, + "user": { + "type": "keyword" + } + } + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "number_of_replicas": "0", + "number_of_shards": "1" + } + } + } +} \ No newline at end of file diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/index.ts b/x-pack/test/plugin_api_integration/test_suites/task_manager/index.ts index b542bff3a4aa9..c1e7aad8ac36f 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/index.ts +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/index.ts @@ -11,5 +11,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { this.tags('ciGroup2'); loadTestFile(require.resolve('./health_route')); loadTestFile(require.resolve('./task_management')); + loadTestFile(require.resolve('./task_management_removed_types')); }); } diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management_removed_types.ts b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management_removed_types.ts new file mode 100644 index 0000000000000..a0ca970bac844 --- /dev/null +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management_removed_types.ts @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import _ from 'lodash'; +import expect from '@kbn/expect'; +import url from 'url'; +import supertestAsPromised from 'supertest-as-promised'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { ConcreteTaskInstance } from '../../../../plugins/task_manager/server'; + +export interface RawDoc { + _id: string; + _source: any; + _type?: string; +} +export interface SearchResults { + hits: { + hits: RawDoc[]; + }; +} + +type DeprecatedConcreteTaskInstance = Omit & { + interval: string; +}; + +type SerializedConcreteTaskInstance = Omit< + ConcreteTaskInstance, + 'state' | 'params' | 'scheduledAt' | 'startedAt' | 'retryAt' | 'runAt' +> & { + state: State; + params: Params; + scheduledAt: string; + startedAt: string | null; + retryAt: string | null; + runAt: string; +}; + +export default function ({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const retry = getService('retry'); + const config = getService('config'); + const supertest = supertestAsPromised(url.format(config.get('servers.kibana'))); + + const REMOVED_TASK_TYPE_ID = 'be7e1250-3322-11eb-94c1-db6995e83f6a'; + + describe('removed task types', () => { + before(async () => { + await esArchiver.load('task_manager_removed_types'); + }); + + after(async () => { + await esArchiver.unload('task_manager_removed_types'); + }); + + function scheduleTask( + task: Partial + ): Promise { + return supertest + .post('/api/sample_tasks/schedule') + .set('kbn-xsrf', 'xxx') + .send({ task }) + .expect(200) + .then((response: { body: SerializedConcreteTaskInstance }) => response.body); + } + + function currentTasks(): Promise<{ + docs: Array>; + }> { + return supertest + .get('/api/sample_tasks') + .expect(200) + .then((response) => response.body); + } + + it('should successfully schedule registered tasks and mark unregistered tasks as unrecognized', async () => { + const scheduledTask = await scheduleTask({ + taskType: 'sampleTask', + schedule: { interval: `1s` }, + params: {}, + }); + + await retry.try(async () => { + const tasks = (await currentTasks()).docs; + expect(tasks.length).to.eql(2); + + const taskIds = tasks.map((task) => task.id); + expect(taskIds).to.contain(scheduledTask.id); + expect(taskIds).to.contain(REMOVED_TASK_TYPE_ID); + + const scheduledTaskInstance = tasks.find((task) => task.id === scheduledTask.id); + const removedTaskInstance = tasks.find((task) => task.id === REMOVED_TASK_TYPE_ID); + + expect(scheduledTaskInstance?.status).to.eql('claiming'); + expect(removedTaskInstance?.status).to.eql('unrecognized'); + }); + }); + }); +} From a20709cce66104f8a8651d07d2f78742713172c9 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Wed, 2 Dec 2020 12:00:14 -0500 Subject: [PATCH 11/40] Deprecate disabling the spaces plugin (#83984) Co-authored-by: Aleh Zasypkin --- docs/developer/plugin-list.asciidoc | 4 +- docs/settings/spaces-settings.asciidoc | 1 + x-pack/plugins/spaces/README.md | 10 ++++ x-pack/plugins/spaces/server/config.test.ts | 63 +++++++++++++++++++++ x-pack/plugins/spaces/server/config.ts | 19 ++++++- x-pack/plugins/spaces/server/index.ts | 9 ++- 6 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 x-pack/plugins/spaces/README.md create mode 100644 x-pack/plugins/spaces/server/config.test.ts diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 5ee7131610584..e515abee6014c 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -507,8 +507,8 @@ Kibana. |or -|{kib-repo}blob/{branch}/x-pack/plugins/spaces[spaces] -|WARNING: Missing README. +|{kib-repo}blob/{branch}/x-pack/plugins/spaces/README.md[spaces] +|See Configuring Kibana Spaces. |{kib-repo}blob/{branch}/x-pack/plugins/stack_alerts/README.md[stackAlerts] diff --git a/docs/settings/spaces-settings.asciidoc b/docs/settings/spaces-settings.asciidoc index bda5f00f762cd..3b643f76f0c09 100644 --- a/docs/settings/spaces-settings.asciidoc +++ b/docs/settings/spaces-settings.asciidoc @@ -16,6 +16,7 @@ roles when Security is enabled. |=== | `xpack.spaces.enabled` | Set to `true` (default) to enable Spaces in {kib}. + This setting is deprecated. Starting in 8.0, it will not be possible to disable this plugin. | `xpack.spaces.maxSpaces` | The maximum amount of Spaces that can be used with this instance of {kib}. Some operations diff --git a/x-pack/plugins/spaces/README.md b/x-pack/plugins/spaces/README.md new file mode 100644 index 0000000000000..89194253dce4a --- /dev/null +++ b/x-pack/plugins/spaces/README.md @@ -0,0 +1,10 @@ +# Kibana Spaces Plugin + +See [Configuring Kibana Spaces](https://www.elastic.co/guide/en/kibana/current/spaces-settings-kb.html). + +The spaces plugin enables Kibana Spaces, which provide isolation and organization +for saved objects. + +Spaces also allow for a creating a curated Kibana experience, by hiding features that aren't relevant to your users. + +Spaces can be combined with Security to further enhance the authorization model. diff --git a/x-pack/plugins/spaces/server/config.test.ts b/x-pack/plugins/spaces/server/config.test.ts new file mode 100644 index 0000000000000..d27498eb6e3fc --- /dev/null +++ b/x-pack/plugins/spaces/server/config.test.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { configDeprecationFactory, applyDeprecations } from '@kbn/config'; +import { deepFreeze } from '@kbn/std'; +import { spacesConfigDeprecationProvider } from './config'; + +const applyConfigDeprecations = (settings: Record = {}) => { + const deprecations = spacesConfigDeprecationProvider(configDeprecationFactory); + const deprecationMessages: string[] = []; + const migrated = applyDeprecations( + settings, + deprecations.map((deprecation) => ({ + deprecation, + path: '', + })), + (msg) => deprecationMessages.push(msg) + ); + return { + messages: deprecationMessages, + migrated, + }; +}; + +describe('spaces config', () => { + describe('deprecations', () => { + describe('enabled', () => { + it('logs a warning if xpack.spaces.enabled is set to false', () => { + const originalConfig = deepFreeze({ xpack: { spaces: { enabled: false } } }); + + const { messages, migrated } = applyConfigDeprecations({ ...originalConfig }); + + expect(messages).toMatchInlineSnapshot(` + Array [ + "Disabling the spaces plugin (xpack.spaces.enabled) will not be supported in the next major version (8.0)", + ] + `); + expect(migrated).toEqual(originalConfig); + }); + + it('does not log a warning if no settings are explicitly set', () => { + const originalConfig = deepFreeze({}); + + const { messages, migrated } = applyConfigDeprecations({ ...originalConfig }); + + expect(messages).toMatchInlineSnapshot(`Array []`); + expect(migrated).toEqual(originalConfig); + }); + + it('does not log a warning if xpack.spaces.enabled is set to true', () => { + const originalConfig = deepFreeze({ xpack: { spaces: { enabled: true } } }); + + const { messages, migrated } = applyConfigDeprecations({ ...originalConfig }); + + expect(messages).toMatchInlineSnapshot(`Array []`); + expect(migrated).toEqual(originalConfig); + }); + }); + }); +}); diff --git a/x-pack/plugins/spaces/server/config.ts b/x-pack/plugins/spaces/server/config.ts index a28624fb82c15..671b725ac1092 100644 --- a/x-pack/plugins/spaces/server/config.ts +++ b/x-pack/plugins/spaces/server/config.ts @@ -5,7 +5,11 @@ */ import { schema, TypeOf } from '@kbn/config-schema'; -import { PluginInitializerContext } from 'src/core/server'; +import type { + PluginInitializerContext, + ConfigDeprecationProvider, + ConfigDeprecation, +} from 'src/core/server'; import { Observable } from 'rxjs'; export const ConfigSchema = schema.object({ @@ -17,6 +21,19 @@ export function createConfig$(context: PluginInitializerContext) { return context.config.create>(); } +const disabledDeprecation: ConfigDeprecation = (config, fromPath, log) => { + if (config.xpack?.spaces?.enabled === false) { + log( + `Disabling the spaces plugin (xpack.spaces.enabled) will not be supported in the next major version (8.0)` + ); + } + return config; +}; + +export const spacesConfigDeprecationProvider: ConfigDeprecationProvider = () => { + return [disabledDeprecation]; +}; + export type ConfigType = ReturnType extends Observable ? P : ReturnType; diff --git a/x-pack/plugins/spaces/server/index.ts b/x-pack/plugins/spaces/server/index.ts index 85f1facf6131c..4d3d184ec41a3 100644 --- a/x-pack/plugins/spaces/server/index.ts +++ b/x-pack/plugins/spaces/server/index.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { PluginInitializerContext } from '../../../../src/core/server'; -import { ConfigSchema } from './config'; +import type { PluginConfigDescriptor, PluginInitializerContext } from '../../../../src/core/server'; +import { ConfigSchema, spacesConfigDeprecationProvider } from './config'; import { Plugin } from './plugin'; // These exports are part of public Spaces plugin contract, any change in signature of exported @@ -22,6 +22,9 @@ export { SpacesServiceSetup, SpacesServiceStart } from './spaces_service'; export { ISpacesClient } from './spaces_client'; export { Space } from '../common/model/space'; -export const config = { schema: ConfigSchema }; +export const config: PluginConfigDescriptor = { + schema: ConfigSchema, + deprecations: spacesConfigDeprecationProvider, +}; export const plugin = (initializerContext: PluginInitializerContext) => new Plugin(initializerContext); From b96b965387898af3480f49860665c5a53d7dc906 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 2 Dec 2020 10:06:33 -0700 Subject: [PATCH 12/40] [build/node] log url when downloading node shasum info (#84692) Co-authored-by: spalger Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- src/dev/build/tasks/nodejs/download_node_builds_task.ts | 2 +- src/dev/build/tasks/nodejs/node_shasums.test.ts | 3 ++- src/dev/build/tasks/nodejs/node_shasums.ts | 5 ++++- .../tasks/nodejs/verify_existing_node_builds_task.test.ts | 2 ++ .../build/tasks/nodejs/verify_existing_node_builds_task.ts | 2 +- 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/dev/build/tasks/nodejs/download_node_builds_task.ts b/src/dev/build/tasks/nodejs/download_node_builds_task.ts index ad42ea11436f5..93ad599e41e40 100644 --- a/src/dev/build/tasks/nodejs/download_node_builds_task.ts +++ b/src/dev/build/tasks/nodejs/download_node_builds_task.ts @@ -25,7 +25,7 @@ export const DownloadNodeBuilds: GlobalTask = { global: true, description: 'Downloading node.js builds for all platforms', async run(config, log) { - const shasums = await getNodeShasums(config.getNodeVersion()); + const shasums = await getNodeShasums(log, config.getNodeVersion()); await Promise.all( config.getNodePlatforms().map(async (platform) => { const { url, downloadPath, downloadName } = getNodeDownloadInfo(config, platform); diff --git a/src/dev/build/tasks/nodejs/node_shasums.test.ts b/src/dev/build/tasks/nodejs/node_shasums.test.ts index 08ac823c7ebf0..53d073afd6499 100644 --- a/src/dev/build/tasks/nodejs/node_shasums.test.ts +++ b/src/dev/build/tasks/nodejs/node_shasums.test.ts @@ -70,11 +70,12 @@ jest.mock('axios', () => ({ }, })); +import { ToolingLog } from '@kbn/dev-utils'; import { getNodeShasums } from './node_shasums'; describe('src/dev/build/tasks/nodejs/node_shasums', () => { it('resolves to an object with shasums for node downloads for version', async () => { - const shasums = await getNodeShasums('8.9.4'); + const shasums = await getNodeShasums(new ToolingLog(), '8.9.4'); expect(shasums).toEqual( expect.objectContaining({ 'node-v8.9.4.tar.gz': '729b44b32b2f82ecd5befac4f7518de0c4e3add34e8fe878f745740a66cbbc01', diff --git a/src/dev/build/tasks/nodejs/node_shasums.ts b/src/dev/build/tasks/nodejs/node_shasums.ts index e0926aa3e49e4..0f506dff4fd88 100644 --- a/src/dev/build/tasks/nodejs/node_shasums.ts +++ b/src/dev/build/tasks/nodejs/node_shasums.ts @@ -18,10 +18,13 @@ */ import axios from 'axios'; +import { ToolingLog } from '@kbn/dev-utils'; -export async function getNodeShasums(nodeVersion: string) { +export async function getNodeShasums(log: ToolingLog, nodeVersion: string) { const url = `https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache/dist/v${nodeVersion}/SHASUMS256.txt`; + log.debug('Downloading shasum values for node version', nodeVersion, 'from', url); + const { status, data } = await axios.get(url); if (status !== 200) { diff --git a/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts b/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts index 9b03dcd828cf9..5b3c1bad74930 100644 --- a/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts +++ b/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts @@ -45,6 +45,7 @@ const testWriter = new ToolingLogCollectingWriter(); log.setWriters([testWriter]); expect.addSnapshotSerializer(createAnyInstanceSerializer(Config)); +expect.addSnapshotSerializer(createAnyInstanceSerializer(ToolingLog)); const nodeVersion = Fs.readFileSync(Path.resolve(REPO_ROOT, '.node-version'), 'utf8').trim(); expect.addSnapshotSerializer( @@ -100,6 +101,7 @@ it('checks shasums for each downloaded node build', async () => { [MockFunction] { "calls": Array [ Array [ + , "", ], ], diff --git a/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.ts b/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.ts index 9ce0778d2d1f0..50684d866cbf5 100644 --- a/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.ts +++ b/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.ts @@ -25,7 +25,7 @@ export const VerifyExistingNodeBuilds: GlobalTask = { global: true, description: 'Verifying previously downloaded node.js build for all platforms', async run(config, log) { - const shasums = await getNodeShasums(config.getNodeVersion()); + const shasums = await getNodeShasums(log, config.getNodeVersion()); await Promise.all( config.getNodePlatforms().map(async (platform) => { From 7f969136a3f3a7b85eb2dcaef7b886953a179a1c Mon Sep 17 00:00:00 2001 From: Bohdan Tsymbala Date: Wed, 2 Dec 2020 18:55:08 +0100 Subject: [PATCH 13/40] Added migration of policy for AV registration config. (#84779) * Added migration of policy for AV registration config. * Updated migration a bit to be more safe. --- .../common/endpoint/policy/migrations/to_v7_11.0.test.ts | 3 ++- .../common/endpoint/policy/migrations/to_v7_11.0.ts | 9 +++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/policy/migrations/to_v7_11.0.test.ts b/x-pack/plugins/security_solution/common/endpoint/policy/migrations/to_v7_11.0.test.ts index 6d3e320fba3af..b516f7c57a96d 100644 --- a/x-pack/plugins/security_solution/common/endpoint/policy/migrations/to_v7_11.0.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/policy/migrations/to_v7_11.0.test.ts @@ -10,7 +10,7 @@ import { migratePackagePolicyToV7110 } from './to_v7_11.0'; describe('7.11.0 Endpoint Package Policy migration', () => { const migration = migratePackagePolicyToV7110; - it('adds malware notification checkbox and optional message', () => { + it('adds malware notification checkbox and optional message and adds AV registration config', () => { const doc: SavedObjectUnsanitizedDoc = { attributes: { name: 'Some Policy Name', @@ -77,6 +77,7 @@ describe('7.11.0 Endpoint Package Policy migration', () => { policy: { value: { windows: { + antivirus_registration: { enabled: false }, popup: { malware: { message: '', diff --git a/x-pack/plugins/security_solution/common/endpoint/policy/migrations/to_v7_11.0.ts b/x-pack/plugins/security_solution/common/endpoint/policy/migrations/to_v7_11.0.ts index 551e0ecfdcb4f..557633a747267 100644 --- a/x-pack/plugins/security_solution/common/endpoint/policy/migrations/to_v7_11.0.ts +++ b/x-pack/plugins/security_solution/common/endpoint/policy/migrations/to_v7_11.0.ts @@ -23,8 +23,13 @@ export const migratePackagePolicyToV7110: SavedObjectMigrationFn Date: Wed, 2 Dec 2020 19:55:58 +0100 Subject: [PATCH 14/40] [Lens] Provide single-value functions to show the "First" or "Last" value of some field (#83437) --- .../workspace_panel/warnings_popover.scss | 17 + .../workspace_panel/warnings_popover.tsx | 57 +++ .../workspace_panel_wrapper.tsx | 66 ++- .../dimension_panel/dimension_editor.scss | 4 + .../dimension_panel/dimension_editor.tsx | 28 +- .../dimension_panel/dimension_panel.test.tsx | 2 +- .../dimension_panel/dimension_panel.tsx | 18 +- .../dimension_panel/droppable.test.ts | 2 - .../indexpattern.test.ts | 12 +- .../indexpattern_datasource/indexpattern.tsx | 41 +- .../indexpattern_suggestions.ts | 6 +- .../indexpattern_datasource/loader.test.ts | 2 - .../operations/__mocks__/index.ts | 1 + .../operations/definitions/cardinality.tsx | 4 + .../operations/definitions/count.tsx | 3 + .../operations/definitions/date_histogram.tsx | 3 + .../operations/definitions/helpers.tsx | 34 ++ .../operations/definitions/index.ts | 40 +- .../definitions/last_value.test.tsx | 477 ++++++++++++++++++ .../operations/definitions/last_value.tsx | 257 ++++++++++ .../operations/definitions/metrics.tsx | 3 + .../operations/definitions/ranges/ranges.tsx | 3 + .../operations/definitions/terms/index.tsx | 5 +- .../definitions/terms/terms.test.tsx | 83 ++- .../operations/layer_helpers.test.ts | 1 - .../operations/mocks.ts | 1 + .../operations/operations.test.ts | 19 + .../public/indexpattern_datasource/utils.ts | 64 +-- .../pie_visualization/visualization.tsx | 28 + x-pack/plugins/lens/public/types.ts | 5 + .../xy_visualization/visualization.test.ts | 65 +++ .../public/xy_visualization/visualization.tsx | 32 ++ 32 files changed, 1256 insertions(+), 127 deletions(-) create mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/warnings_popover.scss create mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/warnings_popover.tsx create mode 100644 x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx create mode 100644 x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/warnings_popover.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/warnings_popover.scss new file mode 100644 index 0000000000000..19f815dfb7114 --- /dev/null +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/warnings_popover.scss @@ -0,0 +1,17 @@ +.lnsWorkspaceWarning__button { + color: $euiColorWarningText; +} + +.lnsWorkspaceWarningList { + @include euiYScroll; + max-height: $euiSize * 20; + width: $euiSize * 16; +} + +.lnsWorkspaceWarningList__item { + padding: $euiSize; + + & + & { + border-top: $euiBorderThin; + } +} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/warnings_popover.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/warnings_popover.tsx new file mode 100644 index 0000000000000..cb414972e84af --- /dev/null +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/warnings_popover.tsx @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import './workspace_panel_wrapper.scss'; +import './warnings_popover.scss'; + +import React, { useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiPopover, EuiText, EuiButtonEmpty } from '@elastic/eui'; + +export const WarningsPopover = ({ + children, +}: { + children?: React.ReactNode | React.ReactNode[]; +}) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + if (!children) { + return null; + } + + const onButtonClick = () => setIsPopoverOpen((isOpen) => !isOpen); + const closePopover = () => setIsPopoverOpen(false); + const warningsCount = React.Children.count(children); + return ( + + {i18n.translate('xpack.lens.chartWarnings.number', { + defaultMessage: `{warningsCount} {warningsCount, plural, one {warning} other {warnings}}`, + values: { + warningsCount, + }, + })} + + } + isOpen={isPopoverOpen} + closePopover={closePopover} + > +
    + {React.Children.map(children, (child, index) => ( +
  • + {child} +
  • + ))} +
+
+ ); +}; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx index d9fbaa22a0388..046bebb33a57d 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx @@ -19,6 +19,7 @@ import { Datasource, FramePublicAPI, Visualization } from '../../../types'; import { NativeRenderer } from '../../../native_renderer'; import { Action } from '../state_management'; import { ChartSwitch } from './chart_switch'; +import { WarningsPopover } from './warnings_popover'; export interface WorkspacePanelWrapperProps { children: React.ReactNode | React.ReactNode[]; @@ -64,40 +65,59 @@ export function WorkspacePanelWrapper({ }, [dispatch, activeVisualization] ); + const warningMessages = + activeVisualization?.getWarningMessages && + activeVisualization.getWarningMessages(visualizationState, framePublicAPI); return ( <>
- + + + + + {activeVisualization && activeVisualization.renderToolbar && ( + + + + )} + + + + {warningMessages && warningMessages.length ? ( + {warningMessages} + ) : null} - {activeVisualization && activeVisualization.renderToolbar && ( - - - - )}
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.scss b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.scss index 096047da681b9..6bd6808f17b35 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.scss +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.scss @@ -15,6 +15,10 @@ column-gap: $euiSizeXL; } +.lnsIndexPatternDimensionEditor__operation .euiListGroupItem__label { + width: 100%; +} + .lnsIndexPatternDimensionEditor__operation > button { padding-top: 0; padding-bottom: 0; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx index 4c3def0e5bc7f..576825e9c960a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx @@ -15,6 +15,7 @@ import { EuiSpacer, EuiListGroupItemProps, EuiFormLabel, + EuiToolTip, } from '@elastic/eui'; import { IndexPatternDimensionEditorProps } from './dimension_panel'; import { OperationSupportMatrix } from './operation_support'; @@ -141,20 +142,19 @@ export function DimensionEditor(props: DimensionEditorProps) { definition.input === 'field' && fieldByOperation[operationType]?.has(selectedColumn.sourceField)) || (selectedColumn && !hasField(selectedColumn) && definition.input === 'none'), + disabledStatus: + definition.getDisabledStatus && + definition.getDisabledStatus(state.indexPatterns[state.currentIndexPatternId]), }; }); - const selectedColumnSourceField = - selectedColumn && 'sourceField' in selectedColumn ? selectedColumn.sourceField : undefined; - - const currentFieldIsInvalid = useMemo( - () => - fieldIsInvalid(selectedColumnSourceField, selectedColumn?.operationType, currentIndexPattern), - [selectedColumnSourceField, selectedColumn?.operationType, currentIndexPattern] - ); + const currentFieldIsInvalid = useMemo(() => fieldIsInvalid(selectedColumn, currentIndexPattern), [ + selectedColumn, + currentIndexPattern, + ]); const sideNavItems: EuiListGroupItemProps[] = operationsWithCompatibility.map( - ({ operationType, compatibleWithCurrentField }) => { + ({ operationType, compatibleWithCurrentField, disabledStatus }) => { const isActive = Boolean( incompleteOperation === operationType || (!incompleteOperation && selectedColumn && selectedColumn.operationType === operationType) @@ -168,7 +168,13 @@ export function DimensionEditor(props: DimensionEditorProps) { } let label: EuiListGroupItemProps['label'] = operationPanels[operationType].displayName; - if (isActive) { + if (disabledStatus) { + label = ( + + {operationPanels[operationType].displayName} + + ); + } else if (isActive) { label = {operationPanels[operationType].displayName}; } @@ -178,6 +184,7 @@ export function DimensionEditor(props: DimensionEditorProps) { color, isActive, size: 's', + isDisabled: !!disabledStatus, className: 'lnsIndexPatternDimensionEditor__operation', 'data-test-subj': `lns-indexPatternDimension-${operationType}${ compatibleWithCurrentField ? '' : ' incompatible' @@ -264,7 +271,6 @@ export function DimensionEditor(props: DimensionEditorProps) { ? currentIndexPattern.getFieldByName(selectedColumn.sourceField) : undefined, }); - setState(mergeLayer({ state, layerId, newLayer })); }, }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx index d4197f9395660..dbfffb5c2bd59 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx @@ -1242,12 +1242,12 @@ describe('IndexPatternDimensionEditorPanel', () => { expect(items.map(({ label }: { label: React.ReactNode }) => label)).toEqual([ 'Average', 'Count', + 'Last value', 'Maximum', 'Median', 'Minimum', 'Sum', 'Unique count', - '\u00a0', ]); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx index 2444a6a81c2a0..20134699d2067 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx @@ -12,7 +12,7 @@ import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { DatasourceDimensionTriggerProps, DatasourceDimensionEditorProps } from '../../types'; import { DataPublicPluginStart } from '../../../../../../src/plugins/data/public'; import { IndexPatternColumn } from '../indexpattern'; -import { fieldIsInvalid } from '../utils'; +import { isColumnInvalid } from '../utils'; import { IndexPatternPrivateState } from '../types'; import { DimensionEditor } from './dimension_editor'; import { DateRange } from '../../../common'; @@ -45,24 +45,22 @@ export const IndexPatternDimensionTriggerComponent = function IndexPatternDimens ) { const layerId = props.layerId; const layer = props.state.layers[layerId]; - const selectedColumn: IndexPatternColumn | null = layer.columns[props.columnId] || null; const currentIndexPattern = props.state.indexPatterns[layer.indexPatternId]; + const { columnId, uniqueLabel } = props; - const selectedColumnSourceField = - selectedColumn && 'sourceField' in selectedColumn ? selectedColumn.sourceField : undefined; - const currentFieldIsInvalid = useMemo( - () => - fieldIsInvalid(selectedColumnSourceField, selectedColumn?.operationType, currentIndexPattern), - [selectedColumnSourceField, selectedColumn?.operationType, currentIndexPattern] + const currentColumnHasErrors = useMemo( + () => isColumnInvalid(layer, columnId, currentIndexPattern), + [layer, columnId, currentIndexPattern] ); - const { columnId, uniqueLabel } = props; + const selectedColumn: IndexPatternColumn | null = layer.columns[props.columnId] || null; + if (!selectedColumn) { return null; } const formattedLabel = wrapOnDot(uniqueLabel); - if (currentFieldIsInvalid) { + if (currentColumnHasErrors) { return ( { expect(messages).toHaveLength(1); expect(messages![0]).toEqual({ shortMessage: 'Invalid reference.', - longMessage: 'Field "bytes" has an invalid reference.', + longMessage: '"Foo" has an invalid reference.', }); }); @@ -844,7 +844,7 @@ describe('IndexPattern Data Source', () => { col2: { dataType: 'number', isBucketed: false, - label: 'Foo', + label: 'Foo2', operationType: 'count', // <= invalid sourceField: 'memory', }, @@ -857,7 +857,7 @@ describe('IndexPattern Data Source', () => { expect(messages).toHaveLength(1); expect(messages![0]).toEqual({ shortMessage: 'Invalid references.', - longMessage: 'Fields "bytes", "memory" have invalid reference.', + longMessage: '"Foo", "Foo2" have invalid reference.', }); }); @@ -882,7 +882,7 @@ describe('IndexPattern Data Source', () => { col2: { dataType: 'number', isBucketed: false, - label: 'Foo', + label: 'Foo2', operationType: 'count', // <= invalid sourceField: 'memory', }, @@ -909,11 +909,11 @@ describe('IndexPattern Data Source', () => { expect(messages).toEqual([ { shortMessage: 'Invalid references on Layer 1.', - longMessage: 'Layer 1 has invalid references in fields "bytes", "memory".', + longMessage: 'Layer 1 has invalid references in "Foo", "Foo2".', }, { shortMessage: 'Invalid reference on Layer 2.', - longMessage: 'Layer 2 has an invalid reference in field "source".', + longMessage: 'Layer 2 has an invalid reference in "Foo".', }, ]); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index 948619c6b07e5..a639ea2c00ac0 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -40,7 +40,7 @@ import { } from './indexpattern_suggestions'; import { - getInvalidFieldsForLayer, + getInvalidColumnsForLayer, getInvalidLayers, isDraggedField, normalizeOperationDataType, @@ -387,7 +387,7 @@ export function getIndexPatternDatasource({ } }) .filter(Boolean) as Array<[number, number]>; - const invalidFieldsPerLayer: string[][] = getInvalidFieldsForLayer( + const invalidColumnsForLayer: string[][] = getInvalidColumnsForLayer( invalidLayers, state.indexPatterns ); @@ -397,33 +397,34 @@ export function getIndexPatternDatasource({ return [ ...layerErrors, ...realIndex.map(([filteredIndex, layerIndex]) => { - const fieldsWithBrokenReferences: string[] = invalidFieldsPerLayer[filteredIndex].map( - (columnId) => { - const column = invalidLayers[filteredIndex].columns[ - columnId - ] as FieldBasedIndexPatternColumn; - return column.sourceField; - } - ); + const columnLabelsWithBrokenReferences: string[] = invalidColumnsForLayer[ + filteredIndex + ].map((columnId) => { + const column = invalidLayers[filteredIndex].columns[ + columnId + ] as FieldBasedIndexPatternColumn; + return column.label; + }); if (originalLayersList.length === 1) { return { shortMessage: i18n.translate( 'xpack.lens.indexPattern.dataReferenceFailureShortSingleLayer', { - defaultMessage: 'Invalid {fields, plural, one {reference} other {references}}.', + defaultMessage: + 'Invalid {columns, plural, one {reference} other {references}}.', values: { - fields: fieldsWithBrokenReferences.length, + columns: columnLabelsWithBrokenReferences.length, }, } ), longMessage: i18n.translate( 'xpack.lens.indexPattern.dataReferenceFailureLongSingleLayer', { - defaultMessage: `{fieldsLength, plural, one {Field} other {Fields}} "{fields}" {fieldsLength, plural, one {has an} other {have}} invalid reference.`, + defaultMessage: `"{columns}" {columnsLength, plural, one {has an} other {have}} invalid reference.`, values: { - fields: fieldsWithBrokenReferences.join('", "'), - fieldsLength: fieldsWithBrokenReferences.length, + columns: columnLabelsWithBrokenReferences.join('", "'), + columnsLength: columnLabelsWithBrokenReferences.length, }, } ), @@ -432,18 +433,18 @@ export function getIndexPatternDatasource({ return { shortMessage: i18n.translate('xpack.lens.indexPattern.dataReferenceFailureShort', { defaultMessage: - 'Invalid {fieldsLength, plural, one {reference} other {references}} on Layer {layer}.', + 'Invalid {columnsLength, plural, one {reference} other {references}} on Layer {layer}.', values: { layer: layerIndex, - fieldsLength: fieldsWithBrokenReferences.length, + columnsLength: columnLabelsWithBrokenReferences.length, }, }), longMessage: i18n.translate('xpack.lens.indexPattern.dataReferenceFailureLong', { - defaultMessage: `Layer {layer} has {fieldsLength, plural, one {an invalid} other {invalid}} {fieldsLength, plural, one {reference} other {references}} in {fieldsLength, plural, one {field} other {fields}} "{fields}".`, + defaultMessage: `Layer {layer} has {columnsLength, plural, one {an invalid} other {invalid}} {columnsLength, plural, one {reference} other {references}} in "{columns}".`, values: { layer: layerIndex, - fields: fieldsWithBrokenReferences.join('", "'), - fieldsLength: fieldsWithBrokenReferences.length, + columns: columnLabelsWithBrokenReferences.join('", "'), + columnsLength: columnLabelsWithBrokenReferences.length, }, }), }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts index 263b4646c9feb..ebac396210a5c 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts @@ -18,7 +18,7 @@ import { IndexPatternColumn, OperationType, } from './operations'; -import { hasField, hasInvalidFields } from './utils'; +import { hasField, hasInvalidColumns } from './utils'; import { IndexPattern, IndexPatternPrivateState, @@ -90,7 +90,7 @@ export function getDatasourceSuggestionsForField( indexPatternId: string, field: IndexPatternField ): IndexPatternSugestion[] { - if (hasInvalidFields(state)) return []; + if (hasInvalidColumns(state)) return []; const layers = Object.keys(state.layers); const layerIds = layers.filter((id) => state.layers[id].indexPatternId === indexPatternId); @@ -331,7 +331,7 @@ function createNewLayerWithMetricAggregation( export function getDatasourceSuggestionsFromCurrentState( state: IndexPatternPrivateState ): Array> { - if (hasInvalidFields(state)) return []; + if (hasInvalidColumns(state)) return []; const layers = Object.entries(state.layers || {}); if (layers.length > 1) { // Return suggestions that reduce the data to each layer individually diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts index adb86253ab28c..29786d9bc68f3 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts @@ -25,8 +25,6 @@ import { import { createMockedRestrictedIndexPattern, createMockedIndexPattern } from './mocks'; import { documentField } from './document_field'; -jest.mock('./operations'); - const createMockStorage = (lastData?: Record) => { return { get: jest.fn().mockImplementation(() => lastData), diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/__mocks__/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/__mocks__/index.ts index 077255b75a769..ff900134df9a1 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/__mocks__/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/__mocks__/index.ts @@ -24,6 +24,7 @@ export const { getOperationResultType, operationDefinitionMap, operationDefinitions, + getInvalidFieldMessage, } = actualOperations; export const { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx index fd3ca4319669e..2dc3946c62a09 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx @@ -8,6 +8,8 @@ import { i18n } from '@kbn/i18n'; import { OperationDefinition } from './index'; import { FormattedIndexPatternColumn, FieldBasedIndexPatternColumn } from './column_types'; +import { getInvalidFieldMessage } from './helpers'; + const supportedTypes = new Set(['string', 'boolean', 'number', 'ip', 'date']); const SCALE = 'ratio'; @@ -42,6 +44,8 @@ export const cardinalityOperation: OperationDefinition + getInvalidFieldMessage(layer.columns[columnId] as FieldBasedIndexPatternColumn, indexPattern), isTransferable: (column, newIndexPattern) => { const newField = newIndexPattern.getFieldByName(column.sourceField); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx index 8cb95de72f97e..02a69ad8e550f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx @@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n'; import { OperationDefinition } from './index'; import { FormattedIndexPatternColumn, FieldBasedIndexPatternColumn } from './column_types'; import { IndexPatternField } from '../../types'; +import { getInvalidFieldMessage } from './helpers'; import { adjustTimeScaleLabelSuffix, adjustTimeScaleOnOtherColumnChange, @@ -29,6 +30,8 @@ export const countOperation: OperationDefinition + getInvalidFieldMessage(layer.columns[columnId] as FieldBasedIndexPatternColumn, indexPattern), onFieldChange: (oldColumn, field) => { return { ...oldColumn, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx index efac9c151a435..ca426fb53a3ab 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx @@ -23,6 +23,7 @@ import { updateColumnParam } from '../layer_helpers'; import { OperationDefinition } from './index'; import { FieldBasedIndexPatternColumn } from './column_types'; import { IndexPatternAggRestrictions, search } from '../../../../../../../src/plugins/data/public'; +import { getInvalidFieldMessage } from './helpers'; const { isValidInterval } = search.aggs; const autoInterval = 'auto'; @@ -46,6 +47,8 @@ export const dateHistogramOperation: OperationDefinition< }), input: 'field', priority: 5, // Highest priority level used + getErrorMessage: (layer, columnId, indexPattern) => + getInvalidFieldMessage(layer.columns[columnId] as FieldBasedIndexPatternColumn, indexPattern), getPossibleOperationForField: ({ aggregationRestrictions, aggregatable, type }) => { if ( type === 'date' && diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx index a5c08a93467af..640a357d9a7a4 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx @@ -6,6 +6,10 @@ import { useRef } from 'react'; import useDebounce from 'react-use/lib/useDebounce'; +import { i18n } from '@kbn/i18n'; +import { operationDefinitionMap } from '.'; +import { FieldBasedIndexPatternColumn } from './column_types'; +import { IndexPattern } from '../../types'; export const useDebounceWithOptions = ( fn: Function, @@ -28,3 +32,33 @@ export const useDebounceWithOptions = ( newDeps ); }; + +export function getInvalidFieldMessage( + column: FieldBasedIndexPatternColumn, + indexPattern?: IndexPattern +) { + if (!indexPattern) { + return; + } + const { sourceField, operationType } = column; + const field = sourceField ? indexPattern.getFieldByName(sourceField) : undefined; + const operationDefinition = operationType && operationDefinitionMap[operationType]; + + const isInvalid = Boolean( + sourceField && + operationDefinition && + !( + field && + operationDefinition?.input === 'field' && + operationDefinition.getPossibleOperationForField(field) !== undefined + ) + ); + return isInvalid + ? [ + i18n.translate('xpack.lens.indexPattern.fieldNotFound', { + defaultMessage: 'Field {invalidField} was not found', + values: { invalidField: sourceField }, + }), + ] + : undefined; +} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts index 31bb332f791da..460c7c5492879 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts @@ -34,6 +34,7 @@ import { MovingAverageIndexPatternColumn, } from './calculations'; import { countOperation, CountIndexPatternColumn } from './count'; +import { lastValueOperation, LastValueIndexPatternColumn } from './last_value'; import { StateSetter, OperationMetadata } from '../../../types'; import type { BaseIndexPatternColumn, ReferenceBasedIndexPatternColumn } from './column_types'; import { @@ -63,6 +64,7 @@ export type IndexPatternColumn = | SumIndexPatternColumn | MedianIndexPatternColumn | CountIndexPatternColumn + | LastValueIndexPatternColumn | CumulativeSumIndexPatternColumn | CounterRateIndexPatternColumn | DerivativeIndexPatternColumn @@ -85,6 +87,7 @@ const internalOperationDefinitions = [ cardinalityOperation, sumOperation, medianOperation, + lastValueOperation, countOperation, rangeOperation, cumulativeSumOperation, @@ -99,6 +102,7 @@ export { filtersOperation } from './filters'; export { dateHistogramOperation } from './date_histogram'; export { minOperation, averageOperation, sumOperation, maxOperation } from './metrics'; export { countOperation } from './count'; +export { lastValueOperation } from './last_value'; export { cumulativeSumOperation, counterRateOperation, @@ -173,6 +177,24 @@ interface BaseOperationDefinitionProps { */ transfer?: (column: C, newIndexPattern: IndexPattern) => C; /** + * if there is some reason to display the operation in the operations list + * but disable it from usage, this function returns the string describing + * the status. Otherwise it returns undefined + */ + getDisabledStatus?: (indexPattern: IndexPattern) => string | undefined; + /** + * Validate that the operation has the right preconditions in the state. For example: + * + * - Requires a date histogram operation somewhere before it in order + * - Missing references + */ + getErrorMessage?: ( + layer: IndexPatternLayer, + columnId: string, + indexPattern?: IndexPattern + ) => string[] | undefined; + + /* * Flag whether this operation can be scaled by time unit if a date histogram is available. * If set to mandatory or optional, a UI element is shown in the config flyout to configure the time unit * to scale by. The chosen unit will be persisted as `timeScale` property of the column. @@ -245,6 +267,17 @@ interface FieldBasedOperationDefinition { * together with the agg configs returned from other columns. */ toEsAggsConfig: (column: C, columnId: string, indexPattern: IndexPattern) => unknown; + /** + * Validate that the operation has the right preconditions in the state. For example: + * + * - Requires a date histogram operation somewhere before it in order + * - Missing references + */ + getErrorMessage: ( + layer: IndexPatternLayer, + columnId: string, + indexPattern?: IndexPattern + ) => string[] | undefined; } export interface RequiredReference { @@ -297,13 +330,6 @@ interface FullReferenceOperationDefinition { columnId: string, indexPattern: IndexPattern ) => ExpressionFunctionAST[]; - /** - * Validate that the operation has the right preconditions in the state. For example: - * - * - Requires a date histogram operation somewhere before it in order - * - Missing references - */ - getErrorMessage?: (layer: IndexPatternLayer, columnId: string) => string[] | undefined; } interface OperationDefinitionMap { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx new file mode 100644 index 0000000000000..09b68e78d3469 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx @@ -0,0 +1,477 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallow } from 'enzyme'; +import { EuiComboBox } from '@elastic/eui'; +import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; +import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks'; +import { createMockedIndexPattern } from '../../mocks'; +import { LastValueIndexPatternColumn } from './last_value'; +import { lastValueOperation } from './index'; +import { IndexPatternPrivateState, IndexPattern, IndexPatternLayer } from '../../types'; + +const defaultProps = { + storage: {} as IStorageWrapper, + uiSettings: {} as IUiSettingsClient, + savedObjectsClient: {} as SavedObjectsClientContract, + dateRange: { fromDate: 'now-1d', toDate: 'now' }, + data: dataPluginMock.createStartContract(), + http: {} as HttpSetup, +}; + +describe('last_value', () => { + let state: IndexPatternPrivateState; + const InlineOptions = lastValueOperation.paramEditor!; + + beforeEach(() => { + const indexPattern = createMockedIndexPattern(); + state = { + indexPatternRefs: [], + indexPatterns: { + '1': { + ...indexPattern, + hasRestrictions: false, + } as IndexPattern, + }, + existingFields: {}, + currentIndexPatternId: '1', + isFirstExistenceFetch: false, + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'Top value of category', + dataType: 'string', + isBucketed: true, + operationType: 'terms', + params: { + orderBy: { type: 'alphabetical' }, + size: 3, + orderDirection: 'asc', + }, + sourceField: 'category', + }, + col2: { + label: 'Last value of a', + dataType: 'number', + isBucketed: false, + sourceField: 'a', + operationType: 'last_value', + params: { + sortField: 'datefield', + }, + }, + }, + }, + }, + }; + }); + + describe('toEsAggsConfig', () => { + it('should reflect params correctly', () => { + const lastValueColumn = state.layers.first.columns.col2 as LastValueIndexPatternColumn; + const esAggsConfig = lastValueOperation.toEsAggsConfig( + { ...lastValueColumn, params: { ...lastValueColumn.params } }, + 'col1', + {} as IndexPattern + ); + expect(esAggsConfig).toEqual( + expect.objectContaining({ + params: expect.objectContaining({ + aggregate: 'concat', + field: 'a', + size: 1, + sortField: 'datefield', + sortOrder: 'desc', + }), + }) + ); + }); + }); + + describe('onFieldChange', () => { + it('should change correctly to new field', () => { + const oldColumn: LastValueIndexPatternColumn = { + operationType: 'last_value', + sourceField: 'source', + label: 'Last value of source', + isBucketed: true, + dataType: 'string', + params: { + sortField: 'datefield', + }, + }; + const indexPattern = createMockedIndexPattern(); + const newNumberField = indexPattern.getFieldByName('bytes')!; + const column = lastValueOperation.onFieldChange(oldColumn, newNumberField); + + expect(column).toEqual( + expect.objectContaining({ + dataType: 'number', + sourceField: 'bytes', + params: expect.objectContaining({ + sortField: 'datefield', + }), + }) + ); + expect(column.label).toContain('bytes'); + }); + + it('should remove numeric parameters when changing away from number', () => { + const oldColumn: LastValueIndexPatternColumn = { + operationType: 'last_value', + sourceField: 'bytes', + label: 'Last value of bytes', + isBucketed: false, + dataType: 'number', + params: { + sortField: 'datefield', + }, + }; + const indexPattern = createMockedIndexPattern(); + const newStringField = indexPattern.fields.find((i) => i.name === 'source')!; + + const column = lastValueOperation.onFieldChange(oldColumn, newStringField); + expect(column).toHaveProperty('dataType', 'string'); + expect(column).toHaveProperty('sourceField', 'source'); + expect(column.params.format).toBeUndefined(); + }); + }); + + describe('getPossibleOperationForField', () => { + it('should return operation with the right type', () => { + expect( + lastValueOperation.getPossibleOperationForField({ + aggregatable: true, + searchable: true, + name: 'test', + displayName: 'test', + type: 'boolean', + }) + ).toEqual({ + dataType: 'boolean', + isBucketed: false, + scale: 'ratio', + }); + + expect( + lastValueOperation.getPossibleOperationForField({ + aggregatable: true, + searchable: true, + name: 'test', + displayName: 'test', + type: 'ip', + }) + ).toEqual({ + dataType: 'ip', + isBucketed: false, + scale: 'ratio', + }); + }); + + it('should not return an operation if restrictions prevent terms', () => { + expect( + lastValueOperation.getPossibleOperationForField({ + aggregatable: true, + searchable: true, + name: 'test', + displayName: 'test', + type: 'string', + aggregationRestrictions: { + terms: { + agg: 'terms', + }, + }, + }) + ).toEqual(undefined); + + expect( + lastValueOperation.getPossibleOperationForField({ + aggregatable: true, + aggregationRestrictions: {}, + searchable: true, + name: 'test', + displayName: 'test', + type: 'string', + }) + ).toEqual(undefined); + // does it have to be aggregatable? + expect( + lastValueOperation.getPossibleOperationForField({ + aggregatable: false, + searchable: true, + name: 'test', + displayName: 'test', + type: 'string', + }) + ).toEqual({ dataType: 'string', isBucketed: false, scale: 'ordinal' }); + }); + }); + + describe('buildColumn', () => { + it('should use type from the passed field', () => { + const lastValueColumn = lastValueOperation.buildColumn({ + indexPattern: createMockedIndexPattern(), + field: { + aggregatable: true, + searchable: true, + type: 'boolean', + name: 'test', + displayName: 'test', + }, + layer: { columns: {}, columnOrder: [], indexPatternId: '' }, + }); + expect(lastValueColumn.dataType).toEqual('boolean'); + }); + + it('should use indexPattern timeFieldName as a default sortField', () => { + const lastValueColumn = lastValueOperation.buildColumn({ + indexPattern: createMockedIndexPattern(), + + layer: { + columns: { + col1: { + label: 'Count', + dataType: 'number', + isBucketed: false, + sourceField: 'Records', + operationType: 'count', + }, + }, + columnOrder: [], + indexPatternId: '', + }, + + field: { + aggregatable: true, + searchable: true, + type: 'boolean', + name: 'test', + displayName: 'test', + }, + }); + expect(lastValueColumn.params).toEqual( + expect.objectContaining({ + sortField: 'timestamp', + }) + ); + }); + + it('should use first indexPattern date field if there is no default timefieldName', () => { + const indexPattern = createMockedIndexPattern(); + const indexPatternNoTimeField = { + ...indexPattern, + timeFieldName: undefined, + fields: [ + { + aggregatable: true, + searchable: true, + type: 'date', + name: 'datefield', + displayName: 'datefield', + }, + { + aggregatable: true, + searchable: true, + type: 'boolean', + name: 'test', + displayName: 'test', + }, + ], + }; + const lastValueColumn = lastValueOperation.buildColumn({ + indexPattern: indexPatternNoTimeField, + + layer: { + columns: { + col1: { + label: 'Count', + dataType: 'number', + isBucketed: false, + sourceField: 'Records', + operationType: 'count', + }, + }, + columnOrder: [], + indexPatternId: '', + }, + + field: { + aggregatable: true, + searchable: true, + type: 'boolean', + name: 'test', + displayName: 'test', + }, + }); + expect(lastValueColumn.params).toEqual( + expect.objectContaining({ + sortField: 'datefield', + }) + ); + }); + }); + + it('should return disabledStatus if indexPattern does contain date field', () => { + const indexPattern = createMockedIndexPattern(); + + expect(lastValueOperation.getDisabledStatus!(indexPattern)).toEqual(undefined); + + const indexPatternWithoutTimeFieldName = { + ...indexPattern, + timeFieldName: undefined, + }; + expect(lastValueOperation.getDisabledStatus!(indexPatternWithoutTimeFieldName)).toEqual( + undefined + ); + + const indexPatternWithoutTimefields = { + ...indexPatternWithoutTimeFieldName, + fields: indexPattern.fields.filter((f) => f.type !== 'date'), + }; + + const disabledStatus = lastValueOperation.getDisabledStatus!(indexPatternWithoutTimefields); + expect(disabledStatus).toEqual( + 'This function requires the presence of a date field in your index' + ); + }); + + describe('param editor', () => { + it('should render current sortField', () => { + const setStateSpy = jest.fn(); + const instance = shallow( + + ); + + const select = instance.find('[data-test-subj="lns-indexPattern-lastValue-sortField"]'); + + expect(select.prop('selectedOptions')).toEqual([{ label: 'datefield', value: 'datefield' }]); + }); + + it('should update state when changing sortField', () => { + const setStateSpy = jest.fn(); + const instance = shallow( + + ); + + instance + .find('[data-test-subj="lns-indexPattern-lastValue-sortField"]') + .find(EuiComboBox) + .prop('onChange')!([{ label: 'datefield2', value: 'datefield2' }]); + + expect(setStateSpy).toHaveBeenCalledWith({ + ...state, + layers: { + first: { + ...state.layers.first, + columns: { + ...state.layers.first.columns, + col2: { + ...state.layers.first.columns.col2, + params: { + ...(state.layers.first.columns.col2 as LastValueIndexPatternColumn).params, + sortField: 'datefield2', + }, + }, + }, + }, + }, + }); + }); + }); + + describe('getErrorMessage', () => { + let indexPattern: IndexPattern; + let layer: IndexPatternLayer; + beforeEach(() => { + indexPattern = createMockedIndexPattern(); + layer = { + columns: { + col1: { + dataType: 'boolean', + isBucketed: false, + label: 'Last value of test', + operationType: 'last_value', + params: { sortField: 'timestamp' }, + scale: 'ratio', + sourceField: 'bytes', + }, + }, + columnOrder: [], + indexPatternId: '', + }; + }); + it('returns undefined if sourceField exists and sortField is of type date ', () => { + expect(lastValueOperation.getErrorMessage!(layer, 'col1', indexPattern)).toEqual(undefined); + }); + it('shows error message if the sourceField does not exist in index pattern', () => { + layer = { + ...layer, + columns: { + col1: { + ...layer.columns.col1, + sourceField: 'notExisting', + } as LastValueIndexPatternColumn, + }, + }; + expect(lastValueOperation.getErrorMessage!(layer, 'col1', indexPattern)).toEqual([ + 'Field notExisting was not found', + ]); + }); + it('shows error message if the sortField does not exist in index pattern', () => { + layer = { + ...layer, + columns: { + col1: { + ...layer.columns.col1, + params: { + ...layer.columns.col1.params, + sortField: 'notExisting', + }, + } as LastValueIndexPatternColumn, + }, + }; + expect(lastValueOperation.getErrorMessage!(layer, 'col1', indexPattern)).toEqual([ + 'Field notExisting was not found', + ]); + }); + it('shows error message if the sortField is not date', () => { + layer = { + ...layer, + columns: { + col1: { + ...layer.columns.col1, + params: { + ...layer.columns.col1.params, + sortField: 'bytes', + }, + } as LastValueIndexPatternColumn, + }, + }; + expect(lastValueOperation.getErrorMessage!(layer, 'col1', indexPattern)).toEqual([ + 'Field bytes is not a date field and cannot be used for sorting', + ]); + }); + }); +}); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx new file mode 100644 index 0000000000000..5ae5dd472ce22 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx @@ -0,0 +1,257 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiFormRow, EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; +import { OperationDefinition } from './index'; +import { FieldBasedIndexPatternColumn } from './column_types'; +import { IndexPatternField, IndexPattern } from '../../types'; +import { updateColumnParam } from '../layer_helpers'; +import { DataType } from '../../../types'; +import { getInvalidFieldMessage } from './helpers'; + +function ofName(name: string) { + return i18n.translate('xpack.lens.indexPattern.lastValueOf', { + defaultMessage: 'Last value of {name}', + values: { name }, + }); +} + +const supportedTypes = new Set(['string', 'boolean', 'number', 'ip']); + +export function getInvalidSortFieldMessage(sortField: string, indexPattern?: IndexPattern) { + if (!indexPattern) { + return; + } + const field = indexPattern.getFieldByName(sortField); + if (!field) { + return i18n.translate('xpack.lens.indexPattern.lastValue.sortFieldNotFound', { + defaultMessage: 'Field {invalidField} was not found', + values: { invalidField: sortField }, + }); + } + if (field.type !== 'date') { + return i18n.translate('xpack.lens.indexPattern.lastValue.invalidTypeSortField', { + defaultMessage: 'Field {invalidField} is not a date field and cannot be used for sorting', + values: { invalidField: sortField }, + }); + } +} + +function isTimeFieldNameDateField(indexPattern: IndexPattern) { + return ( + indexPattern.timeFieldName && + indexPattern.fields.find( + (field) => field.name === indexPattern.timeFieldName && field.type === 'date' + ) + ); +} + +function getDateFields(indexPattern: IndexPattern): IndexPatternField[] { + const dateFields = indexPattern.fields.filter((field) => field.type === 'date'); + if (isTimeFieldNameDateField(indexPattern)) { + dateFields.sort(({ name: nameA }, { name: nameB }) => { + if (nameA === indexPattern.timeFieldName) { + return -1; + } + if (nameB === indexPattern.timeFieldName) { + return 1; + } + return 0; + }); + } + return dateFields; +} + +export interface LastValueIndexPatternColumn extends FieldBasedIndexPatternColumn { + operationType: 'last_value'; + params: { + sortField: string; + // last value on numeric fields can be formatted + format?: { + id: string; + params?: { + decimals: number; + }; + }; + }; +} + +export const lastValueOperation: OperationDefinition = { + type: 'last_value', + displayName: i18n.translate('xpack.lens.indexPattern.lastValue', { + defaultMessage: 'Last value', + }), + getDefaultLabel: (column, indexPattern) => + indexPattern.getFieldByName(column.sourceField)!.displayName, + input: 'field', + onFieldChange: (oldColumn, field) => { + const newParams = { ...oldColumn.params }; + + if ('format' in newParams && field.type !== 'number') { + delete newParams.format; + } + return { + ...oldColumn, + dataType: field.type as DataType, + label: ofName(field.displayName), + sourceField: field.name, + params: newParams, + }; + }, + getPossibleOperationForField: ({ aggregationRestrictions, type }) => { + if (supportedTypes.has(type) && !aggregationRestrictions) { + return { + dataType: type as DataType, + isBucketed: false, + scale: type === 'string' ? 'ordinal' : 'ratio', + }; + } + }, + getDisabledStatus(indexPattern: IndexPattern) { + const hasDateFields = indexPattern && getDateFields(indexPattern).length; + if (!hasDateFields) { + return i18n.translate('xpack.lens.indexPattern.lastValue.disabled', { + defaultMessage: 'This function requires the presence of a date field in your index', + }); + } + }, + getErrorMessage(layer, columnId, indexPattern) { + const column = layer.columns[columnId] as LastValueIndexPatternColumn; + let errorMessages: string[] = []; + const invalidSourceFieldMessage = getInvalidFieldMessage(column, indexPattern); + const invalidSortFieldMessage = getInvalidSortFieldMessage( + column.params.sortField, + indexPattern + ); + if (invalidSourceFieldMessage) { + errorMessages = [...invalidSourceFieldMessage]; + } + if (invalidSortFieldMessage) { + errorMessages = [invalidSortFieldMessage]; + } + return errorMessages.length ? errorMessages : undefined; + }, + buildColumn({ field, previousColumn, indexPattern }) { + const sortField = isTimeFieldNameDateField(indexPattern) + ? indexPattern.timeFieldName + : indexPattern.fields.find((f) => f.type === 'date')?.name; + + if (!sortField) { + throw new Error( + i18n.translate('xpack.lens.functions.lastValue.missingSortField', { + defaultMessage: 'This index pattern does not contain any date fields', + }) + ); + } + + return { + label: ofName(field.displayName), + dataType: field.type as DataType, + operationType: 'last_value', + isBucketed: false, + scale: field.type === 'string' ? 'ordinal' : 'ratio', + sourceField: field.name, + params: { + sortField, + }, + }; + }, + toEsAggsConfig: (column, columnId) => ({ + id: columnId, + enabled: true, + schema: 'metric', + type: 'top_hits', + params: { + field: column.sourceField, + aggregate: 'concat', + size: 1, + sortOrder: 'desc', + sortField: column.params.sortField, + }, + }), + + isTransferable: (column, newIndexPattern) => { + const newField = newIndexPattern.getFieldByName(column.sourceField); + const newTimeField = newIndexPattern.getFieldByName(column.params.sortField); + return Boolean( + newField && + newField.type === column.dataType && + !newField.aggregationRestrictions && + newTimeField?.type === 'date' + ); + }, + + paramEditor: ({ state, setState, currentColumn, layerId }) => { + const currentIndexPattern = state.indexPatterns[state.layers[layerId].indexPatternId]; + const dateFields = getDateFields(currentIndexPattern); + const isSortFieldInvalid = !!getInvalidSortFieldMessage( + currentColumn.params.sortField, + currentIndexPattern + ); + return ( + <> + + { + return { + value: field.name, + label: field.displayName, + }; + })} + onChange={(choices) => { + if (choices.length === 0) { + return; + } + setState( + updateColumnParam({ + state, + layerId, + currentColumn, + paramName: 'sortField', + value: choices[0].value, + }) + ); + }} + selectedOptions={ + ((currentColumn.params?.sortField + ? [ + { + label: + currentIndexPattern.getFieldByName(currentColumn.params.sortField) + ?.displayName || currentColumn.params.sortField, + value: currentColumn.params.sortField, + }, + ] + : []) as unknown) as EuiComboBoxOptionOption[] + } + /> + + + ); + }, +}; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx index 45ba721981ed5..10a0b915b552d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx @@ -6,6 +6,7 @@ import { i18n } from '@kbn/i18n'; import { OperationDefinition } from './index'; +import { getInvalidFieldMessage } from './helpers'; import { FormattedIndexPatternColumn, FieldBasedIndexPatternColumn, @@ -103,6 +104,8 @@ function buildMetricOperation>({ missing: 0, }, }), + getErrorMessage: (layer, columnId, indexPattern) => + getInvalidFieldMessage(layer.columns[columnId] as FieldBasedIndexPatternColumn, indexPattern), } as OperationDefinition; } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx index d2456e1c8d375..f2d3435cc52c0 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx @@ -17,6 +17,7 @@ import { mergeLayer } from '../../../state_helpers'; import { supportedFormats } from '../../../format_column'; import { MODES, AUTO_BARS, DEFAULT_INTERVAL, MIN_HISTOGRAM_BARS, SLICES } from './constants'; import { IndexPattern, IndexPatternField } from '../../../types'; +import { getInvalidFieldMessage } from '../helpers'; type RangeType = Omit; // Try to cover all possible serialized states for ranges @@ -109,6 +110,8 @@ export const rangeOperation: OperationDefinition + getInvalidFieldMessage(layer.columns[columnId] as FieldBasedIndexPatternColumn, indexPattern), getPossibleOperationForField: ({ aggregationRestrictions, aggregatable, type }) => { if ( type === 'number' && diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx index 7c69a70c09351..e8351ea1e1d09 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx @@ -22,6 +22,7 @@ import { DataType } from '../../../../types'; import { OperationDefinition } from '../index'; import { FieldBasedIndexPatternColumn } from '../column_types'; import { ValuesRangeInput } from './values_range_input'; +import { getInvalidFieldMessage } from '../helpers'; function ofName(name: string) { return i18n.translate('xpack.lens.indexPattern.termsOf', { @@ -31,7 +32,7 @@ function ofName(name: string) { } function isSortableByColumn(column: IndexPatternColumn) { - return !column.isBucketed; + return !column.isBucketed && column.operationType !== 'last_value'; } const DEFAULT_SIZE = 3; @@ -71,6 +72,8 @@ export const termsOperation: OperationDefinition + getInvalidFieldMessage(layer.columns[columnId] as FieldBasedIndexPatternColumn, indexPattern), isTransferable: (column, newIndexPattern) => { const newField = newIndexPattern.getFieldByName(column.sourceField); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx index e43c7bbd2f72e..0af0f9a9d8613 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx @@ -15,7 +15,7 @@ import { createMockedIndexPattern } from '../../../mocks'; import { ValuesRangeInput } from './values_range_input'; import { TermsIndexPatternColumn } from '.'; import { termsOperation } from '../index'; -import { IndexPatternPrivateState, IndexPattern } from '../../../types'; +import { IndexPatternPrivateState, IndexPattern, IndexPatternLayer } from '../../../types'; const defaultProps = { storage: {} as IStorageWrapper, @@ -368,7 +368,7 @@ describe('terms', () => { }); describe('onOtherColumnChanged', () => { - it('should keep the column if order by column still exists and is metric', () => { + it('should keep the column if order by column still exists and is isSortableByColumn metric', () => { const initialColumn: TermsIndexPatternColumn = { label: 'Top value of category', dataType: 'string', @@ -395,6 +395,40 @@ describe('terms', () => { expect(updatedColumn).toBe(initialColumn); }); + it('should switch to alphabetical ordering if metric is of type last_value', () => { + const initialColumn: TermsIndexPatternColumn = { + label: 'Top value of category', + dataType: 'string', + isBucketed: true, + + // Private + operationType: 'terms', + params: { + orderBy: { type: 'column', columnId: 'col1' }, + size: 3, + orderDirection: 'asc', + }, + sourceField: 'category', + }; + const updatedColumn = termsOperation.onOtherColumnChanged!(initialColumn, { + col1: { + label: 'Last Value', + dataType: 'number', + isBucketed: false, + sourceField: 'bytes', + operationType: 'last_value', + params: { + sortField: 'time', + }, + }, + }); + expect(updatedColumn.params).toEqual( + expect.objectContaining({ + orderBy: { type: 'alphabetical' }, + }) + ); + }); + it('should switch to alphabetical ordering if there are no columns to order by', () => { const termsColumn = termsOperation.onOtherColumnChanged!( { @@ -770,4 +804,49 @@ describe('terms', () => { }); }); }); + describe('getErrorMessage', () => { + let indexPattern: IndexPattern; + let layer: IndexPatternLayer; + beforeEach(() => { + indexPattern = createMockedIndexPattern(); + layer = { + columns: { + col1: { + dataType: 'boolean', + isBucketed: true, + label: 'Top values of bytes', + operationType: 'terms', + params: { + missingBucket: false, + orderBy: { type: 'alphabetical' }, + orderDirection: 'asc', + otherBucket: true, + size: 5, + }, + scale: 'ordinal', + sourceField: 'bytes', + }, + }, + columnOrder: [], + indexPatternId: '', + }; + }); + it('returns undefined if sourceField exists in index pattern', () => { + expect(termsOperation.getErrorMessage!(layer, 'col1', indexPattern)).toEqual(undefined); + }); + it('returns error message if the sourceField does not exist in index pattern', () => { + layer = { + ...layer, + columns: { + col1: { + ...layer.columns.col1, + sourceField: 'notExisting', + } as TermsIndexPatternColumn, + }, + }; + expect(termsOperation.getErrorMessage!(layer, 'col1', indexPattern)).toEqual([ + 'Field notExisting was not found', + ]); + }); + }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts index 7ffbeac39c6f5..93447053a6029 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts @@ -1706,7 +1706,6 @@ describe('state_helpers', () => { describe('getErrorMessages', () => { it('should collect errors from the operation definitions', () => { const mock = jest.fn().mockReturnValue(['error 1']); - // @ts-expect-error not statically analyzed operationDefinitionMap.testReference.getErrorMessage = mock; const errors = getErrorMessages({ indexPatternId: '1', diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/mocks.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/mocks.ts index c3f7dac03ada3..33af8842648f8 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/mocks.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/mocks.ts @@ -35,5 +35,6 @@ export const createMockedReferenceOperation = () => { toExpression: jest.fn().mockReturnValue([]), getPossibleOperation: jest.fn().mockReturnValue({ dataType: 'number', isBucketed: false }), getDefaultLabel: jest.fn().mockReturnValue('Default label'), + getErrorMessage: jest.fn(), }; }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts index 63d0fd3d4e5c5..9f2b8eab4e09b 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts @@ -293,6 +293,25 @@ describe('getOperationTypesForField', () => { "operationType": "median", "type": "field", }, + Object { + "field": "bytes", + "operationType": "last_value", + "type": "field", + }, + ], + }, + Object { + "operationMetaData": Object { + "dataType": "string", + "isBucketed": false, + "scale": "ordinal", + }, + "operations": Array [ + Object { + "field": "source", + "operationType": "last_value", + "type": "field", + }, ], }, ] diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/utils.ts b/x-pack/plugins/lens/public/indexpattern_datasource/utils.ts index 01b834610eb1a..5f4865ca0ac32 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/utils.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/utils.ts @@ -11,7 +11,9 @@ import { BaseIndexPatternColumn, FieldBasedIndexPatternColumn, } from './operations/definitions/column_types'; -import { operationDefinitionMap, OperationType } from './operations'; +import { operationDefinitionMap, IndexPatternColumn } from './operations'; + +import { getInvalidFieldMessage } from './operations/definitions/helpers'; /** * Normalizes the specified operation type. (e.g. document operations @@ -42,60 +44,46 @@ export function isDraggedField(fieldCandidate: unknown): fieldCandidate is Dragg ); } -export function hasInvalidFields(state: IndexPatternPrivateState) { +export function hasInvalidColumns(state: IndexPatternPrivateState) { return getInvalidLayers(state).length > 0; } export function getInvalidLayers(state: IndexPatternPrivateState) { return Object.values(state.layers).filter((layer) => { - return layer.columnOrder.some((columnId) => { - const column = layer.columns[columnId]; - return ( - hasField(column) && - fieldIsInvalid( - column.sourceField, - column.operationType, - state.indexPatterns[layer.indexPatternId] - ) - ); - }); + return layer.columnOrder.some((columnId) => + isColumnInvalid(layer, columnId, state.indexPatterns[layer.indexPatternId]) + ); }); } -export function getInvalidFieldsForLayer( +export function getInvalidColumnsForLayer( layers: IndexPatternLayer[], indexPatternMap: Record ) { return layers.map((layer) => { - return layer.columnOrder.filter((columnId) => { - const column = layer.columns[columnId]; - return ( - hasField(column) && - fieldIsInvalid( - column.sourceField, - column.operationType, - indexPatternMap[layer.indexPatternId] - ) - ); - }); + return layer.columnOrder.filter((columnId) => + isColumnInvalid(layer, columnId, indexPatternMap[layer.indexPatternId]) + ); }); } -export function fieldIsInvalid( - sourceField: string | undefined, - operationType: OperationType | undefined, +export function isColumnInvalid( + layer: IndexPatternLayer, + columnId: string, indexPattern: IndexPattern ) { - const operationDefinition = operationType && operationDefinitionMap[operationType]; - const field = sourceField ? indexPattern.getFieldByName(sourceField) : undefined; + const column = layer.columns[columnId]; - return Boolean( - sourceField && - operationDefinition && - !( - field && - operationDefinition?.input === 'field' && - operationDefinition.getPossibleOperationForField(field) !== undefined - ) + const operationDefinition = column.operationType && operationDefinitionMap[column.operationType]; + return !!( + operationDefinition.getErrorMessage && + operationDefinition.getErrorMessage(layer, columnId, indexPattern) ); } + +export function fieldIsInvalid(column: IndexPatternColumn | undefined, indexPattern: IndexPattern) { + if (!column || !hasField(column)) { + return false; + } + return !!getInvalidFieldMessage(column, indexPattern)?.length; +} diff --git a/x-pack/plugins/lens/public/pie_visualization/visualization.tsx b/x-pack/plugins/lens/public/pie_visualization/visualization.tsx index 91f0ddb54ad41..2d9a345b978ec 100644 --- a/x-pack/plugins/lens/public/pie_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/visualization.tsx @@ -245,6 +245,34 @@ export const getPieVisualization = ({ ); }, + getWarningMessages(state, frame) { + if (state?.layers.length === 0 || !frame.activeData) { + return; + } + + const metricColumnsWithArrayValues = []; + + for (const layer of state.layers) { + const { layerId, metric } = layer; + const rows = frame.activeData[layerId] && frame.activeData[layerId].rows; + if (!rows || !metric) { + break; + } + const columnToLabel = frame.datasourceLayers[layerId].getOperationForColumnId(metric)?.label; + + const hasArrayValues = rows.some((row) => Array.isArray(row[metric])); + if (hasArrayValues) { + metricColumnsWithArrayValues.push(columnToLabel || metric); + } + } + return metricColumnsWithArrayValues.map((label) => ( + <> + {label} contains array values. Your visualization may not render as + expected. + + )); + }, + getErrorMessages(state, frame) { // not possible to break it? return undefined; diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 2b3cd02c09d1e..ba459a73ea0ee 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -598,6 +598,11 @@ export interface Visualization { state: T, frame: FramePublicAPI ) => Array<{ shortMessage: string; longMessage: string }> | undefined; + + /** + * The frame calls this function to display warnings about visualization + */ + getWarningMessages?: (state: T, frame: FramePublicAPI) => React.ReactNode[] | undefined; } export interface LensFilterEvent { diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts index 546cf06d4014e..d780ce85bad69 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts @@ -775,4 +775,69 @@ describe('xy_visualization', () => { ]); }); }); + + describe('#getWarningMessages', () => { + let mockDatasource: ReturnType; + let frame: ReturnType; + + beforeEach(() => { + frame = createMockFramePublicAPI(); + mockDatasource = createMockDatasource('testDatasource'); + + mockDatasource.publicAPIMock.getTableSpec.mockReturnValue([ + { columnId: 'd' }, + { columnId: 'a' }, + { columnId: 'b' }, + { columnId: 'c' }, + ]); + + frame.datasourceLayers = { + first: mockDatasource.publicAPIMock, + }; + + frame.activeData = { + first: { + type: 'datatable', + columns: [ + { id: 'a', name: 'A', meta: { type: 'number' } }, + { id: 'b', name: 'B', meta: { type: 'number' } }, + ], + rows: [ + { a: 1, b: [2, 0] }, + { a: 3, b: 4 }, + { a: 5, b: 6 }, + { a: 7, b: 8 }, + ], + }, + }; + }); + it('should return a warning when numeric accessors contain array', () => { + (frame.datasourceLayers.first.getOperationForColumnId as jest.Mock).mockReturnValue({ + label: 'Label B', + }); + const warningMessages = xyVisualization.getWarningMessages!( + { + ...exampleState(), + layers: [ + { + layerId: 'first', + seriesType: 'area', + xAccessor: 'a', + accessors: ['b'], + }, + ], + }, + frame + ); + expect(warningMessages).toHaveLength(1); + expect(warningMessages && warningMessages[0]).toMatchInlineSnapshot(` + + + Label B + + contains array values. Your visualization may not render as expected. + + `); + }); + }); }); diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index f0dcaf589b1c4..ebf80c61e0cd1 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -22,6 +22,7 @@ import { LensIconChartBarStacked } from '../assets/chart_bar_stacked'; import { LensIconChartMixedXy } from '../assets/chart_mixed_xy'; import { LensIconChartBarHorizontal } from '../assets/chart_bar_horizontal'; import { getAccessorColorConfig, getColorAssignments } from './color_assignment'; +import { getColumnToLabelMap } from './state_helpers'; const defaultIcon = LensIconChartBarStacked; const defaultSeriesType = 'bar_stacked'; @@ -371,6 +372,37 @@ export const getXyVisualization = ({ return errors.length ? errors : undefined; }, + + getWarningMessages(state, frame) { + if (state?.layers.length === 0 || !frame.activeData) { + return; + } + + const layers = state.layers; + + const filteredLayers = layers.filter(({ accessors }: LayerConfig) => accessors.length > 0); + const accessorsWithArrayValues = []; + for (const layer of filteredLayers) { + const { layerId, accessors } = layer; + const rows = frame.activeData[layerId] && frame.activeData[layerId].rows; + if (!rows) { + break; + } + const columnToLabel = getColumnToLabelMap(layer, frame.datasourceLayers[layerId]); + for (const accessor of accessors) { + const hasArrayValues = rows.some((row) => Array.isArray(row[accessor])); + if (hasArrayValues) { + accessorsWithArrayValues.push(columnToLabel[accessor]); + } + } + } + return accessorsWithArrayValues.map((label) => ( + <> + {label} contains array values. Your visualization may not render as + expected. + + )); + }, }); function validateLayersForDimension( From 88359b742afd0e32acfc1ef64b1159d624714be5 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Wed, 2 Dec 2020 22:15:25 +0300 Subject: [PATCH 15/40] Improve ui settings performance (#84513) * remove unused parameter in "read" function * add cache for uiSettings client * add tests for ui_settings client caching * address comments * do not mutate ui_settings_client cache --- src/core/server/ui_settings/cache.test.ts | 50 ++++++++ src/core/server/ui_settings/cache.ts | 48 ++++++++ .../ui_settings/ui_settings_client.test.ts | 107 ++++++++++++++++++ .../server/ui_settings/ui_settings_client.ts | 46 ++++---- 4 files changed, 227 insertions(+), 24 deletions(-) create mode 100644 src/core/server/ui_settings/cache.test.ts create mode 100644 src/core/server/ui_settings/cache.ts diff --git a/src/core/server/ui_settings/cache.test.ts b/src/core/server/ui_settings/cache.test.ts new file mode 100644 index 0000000000000..ea375751fe437 --- /dev/null +++ b/src/core/server/ui_settings/cache.test.ts @@ -0,0 +1,50 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { Cache } from './cache'; + +describe('Cache', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.clearAllTimers(); + }); + it('stores value for maxAge ms', async () => { + const cache = new Cache(500); + cache.set(42); + expect(cache.get()).toBe(42); + jest.advanceTimersByTime(100); + expect(cache.get()).toBe(42); + }); + it('invalidates cache after maxAge ms', async () => { + const cache = new Cache(500); + cache.set(42); + expect(cache.get()).toBe(42); + jest.advanceTimersByTime(1000); + expect(cache.get()).toBe(null); + }); + it('del invalidates cache immediately', async () => { + const cache = new Cache(10); + cache.set(42); + expect(cache.get()).toBe(42); + cache.del(); + expect(cache.get()).toBe(null); + }); +}); diff --git a/src/core/server/ui_settings/cache.ts b/src/core/server/ui_settings/cache.ts new file mode 100644 index 0000000000000..697cf2b284c78 --- /dev/null +++ b/src/core/server/ui_settings/cache.ts @@ -0,0 +1,48 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +const oneSec = 1000; +const defMaxAge = 5 * oneSec; +/** + * @internal + */ +export class Cache> { + private value: T | null; + private timer?: NodeJS.Timeout; + + /** + * Delete cached value after maxAge ms. + */ + constructor(private readonly maxAge: number = defMaxAge) { + this.value = null; + } + get() { + return this.value; + } + set(value: T) { + this.del(); + this.value = value; + this.timer = setTimeout(() => this.del(), this.maxAge); + } + del() { + if (this.timer) { + clearTimeout(this.timer); + } + this.value = null; + } +} diff --git a/src/core/server/ui_settings/ui_settings_client.test.ts b/src/core/server/ui_settings/ui_settings_client.test.ts index a38fb2ab7e06c..8238511e27ed9 100644 --- a/src/core/server/ui_settings/ui_settings_client.test.ts +++ b/src/core/server/ui_settings/ui_settings_client.test.ts @@ -676,4 +676,111 @@ describe('ui settings', () => { expect(uiSettings.isOverridden('bar')).toBe(true); }); }); + + describe('caching', () => { + describe('read operations cache user config', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.clearAllTimers(); + }); + + it('get', async () => { + const esDocSource = {}; + const { uiSettings, savedObjectsClient } = setup({ esDocSource }); + + await uiSettings.get('any'); + expect(savedObjectsClient.get).toHaveBeenCalledTimes(1); + + await uiSettings.get('foo'); + expect(savedObjectsClient.get).toHaveBeenCalledTimes(1); + + jest.advanceTimersByTime(10000); + await uiSettings.get('foo'); + expect(savedObjectsClient.get).toHaveBeenCalledTimes(2); + }); + + it('getAll', async () => { + const esDocSource = {}; + const { uiSettings, savedObjectsClient } = setup({ esDocSource }); + + await uiSettings.getAll(); + expect(savedObjectsClient.get).toHaveBeenCalledTimes(1); + + await uiSettings.getAll(); + expect(savedObjectsClient.get).toHaveBeenCalledTimes(1); + + jest.advanceTimersByTime(10000); + await uiSettings.getAll(); + expect(savedObjectsClient.get).toHaveBeenCalledTimes(2); + }); + + it('getUserProvided', async () => { + const esDocSource = {}; + const { uiSettings, savedObjectsClient } = setup({ esDocSource }); + + await uiSettings.getUserProvided(); + expect(savedObjectsClient.get).toHaveBeenCalledTimes(1); + + await uiSettings.getUserProvided(); + expect(savedObjectsClient.get).toHaveBeenCalledTimes(1); + + jest.advanceTimersByTime(10000); + await uiSettings.getUserProvided(); + expect(savedObjectsClient.get).toHaveBeenCalledTimes(2); + }); + }); + + describe('write operations invalidate user config cache', () => { + it('set', async () => { + const esDocSource = {}; + const { uiSettings, savedObjectsClient } = setup({ esDocSource }); + + await uiSettings.get('any'); + expect(savedObjectsClient.get).toHaveBeenCalledTimes(1); + + await uiSettings.set('foo', 'bar'); + await uiSettings.get('foo'); + expect(savedObjectsClient.get).toHaveBeenCalledTimes(2); + }); + + it('setMany', async () => { + const esDocSource = {}; + const { uiSettings, savedObjectsClient } = setup({ esDocSource }); + + await uiSettings.get('any'); + expect(savedObjectsClient.get).toHaveBeenCalledTimes(1); + + await uiSettings.setMany({ foo: 'bar' }); + await uiSettings.get('foo'); + expect(savedObjectsClient.get).toHaveBeenCalledTimes(2); + }); + + it('remove', async () => { + const esDocSource = {}; + const { uiSettings, savedObjectsClient } = setup({ esDocSource }); + + await uiSettings.get('any'); + expect(savedObjectsClient.get).toHaveBeenCalledTimes(1); + + await uiSettings.remove('foo'); + await uiSettings.get('foo'); + expect(savedObjectsClient.get).toHaveBeenCalledTimes(2); + }); + + it('removeMany', async () => { + const esDocSource = {}; + const { uiSettings, savedObjectsClient } = setup({ esDocSource }); + + await uiSettings.get('any'); + expect(savedObjectsClient.get).toHaveBeenCalledTimes(1); + + await uiSettings.removeMany(['foo', 'bar']); + await uiSettings.get('foo'); + expect(savedObjectsClient.get).toHaveBeenCalledTimes(2); + }); + }); + }); }); diff --git a/src/core/server/ui_settings/ui_settings_client.ts b/src/core/server/ui_settings/ui_settings_client.ts index f168784a93330..ab5fca9f81031 100644 --- a/src/core/server/ui_settings/ui_settings_client.ts +++ b/src/core/server/ui_settings/ui_settings_client.ts @@ -24,6 +24,7 @@ import { Logger } from '../logging'; import { createOrUpgradeSavedConfig } from './create_or_upgrade_saved_config'; import { IUiSettingsClient, UiSettingsParams, PublicUiSettingsParams } from './types'; import { CannotOverrideError } from './ui_settings_errors'; +import { Cache } from './cache'; export interface UiSettingsServiceOptions { type: string; @@ -36,7 +37,6 @@ export interface UiSettingsServiceOptions { } interface ReadOptions { - ignore401Errors?: boolean; autoCreateOrUpgradeIfMissing?: boolean; } @@ -58,6 +58,7 @@ export class UiSettingsClient implements IUiSettingsClient { private readonly overrides: NonNullable; private readonly defaults: NonNullable; private readonly log: Logger; + private readonly cache: Cache; constructor(options: UiSettingsServiceOptions) { const { type, id, buildNum, savedObjectsClient, log, defaults = {}, overrides = {} } = options; @@ -69,6 +70,7 @@ export class UiSettingsClient implements IUiSettingsClient { this.defaults = defaults; this.overrides = overrides; this.log = log; + this.cache = new Cache(); } getRegistered() { @@ -95,7 +97,12 @@ export class UiSettingsClient implements IUiSettingsClient { } async getUserProvided(): Promise> { - const userProvided: UserProvided = this.onReadHook(await this.read()); + const cachedValue = this.cache.get(); + if (cachedValue) { + return cachedValue; + } + + const userProvided: UserProvided = this.onReadHook(await this.read()); // write all overridden keys, dropping the userValue is override is null and // adding keys for overrides that are not in saved object @@ -104,10 +111,13 @@ export class UiSettingsClient implements IUiSettingsClient { value === null ? { isOverridden: true } : { isOverridden: true, userValue: value }; } + this.cache.set(userProvided); + return userProvided; } async setMany(changes: Record) { + this.cache.del(); this.onWriteHook(changes); await this.write({ changes }); } @@ -140,7 +150,7 @@ export class UiSettingsClient implements IUiSettingsClient { private async getRaw(): Promise { const userProvided = await this.getUserProvided(); - return defaultsDeep(userProvided, this.defaults); + return defaultsDeep({}, userProvided, this.defaults); } private validateKey(key: string, value: unknown) { @@ -209,10 +219,9 @@ export class UiSettingsClient implements IUiSettingsClient { } } - private async read({ - ignore401Errors = false, - autoCreateOrUpgradeIfMissing = true, - }: ReadOptions = {}): Promise> { + private async read({ autoCreateOrUpgradeIfMissing = true }: ReadOptions = {}): Promise< + Record + > { try { const resp = await this.savedObjectsClient.get>(this.type, this.id); return resp.attributes; @@ -227,16 +236,13 @@ export class UiSettingsClient implements IUiSettingsClient { }); if (!failedUpgradeAttributes) { - return await this.read({ - ignore401Errors, - autoCreateOrUpgradeIfMissing: false, - }); + return await this.read({ autoCreateOrUpgradeIfMissing: false }); } return failedUpgradeAttributes; } - if (this.isIgnorableError(error, ignore401Errors)) { + if (this.isIgnorableError(error)) { return {}; } @@ -244,17 +250,9 @@ export class UiSettingsClient implements IUiSettingsClient { } } - private isIgnorableError(error: Error, ignore401Errors: boolean) { - const { - isForbiddenError, - isEsUnavailableError, - isNotAuthorizedError, - } = this.savedObjectsClient.errors; - - return ( - isForbiddenError(error) || - isEsUnavailableError(error) || - (ignore401Errors && isNotAuthorizedError(error)) - ); + private isIgnorableError(error: Error) { + const { isForbiddenError, isEsUnavailableError } = this.savedObjectsClient.errors; + + return isForbiddenError(error) || isEsUnavailableError(error); } } From b6913a3d2e5e3833ec391af4cb7dae511bc8ef21 Mon Sep 17 00:00:00 2001 From: Scotty Bollinger Date: Wed, 2 Dec 2020 13:34:07 -0600 Subject: [PATCH 16/40] [Enterprise Search] Convert IndexingStatus to use logic for fetching (#84710) * Add IndexingStatusLogic * Replace IndexingStatusFetcher with logic * Refactor out unnecessary conditional onComplete is not optional so these if blocks can be consolidated * Misc styling - destructuring and typing Co-authored-by: Constance * Misc styling - imports Co-authored-by: Constance * Remove div * Refactor test * Replace method with string for statusPath In ent-search, we use Rails helpers to generate paths. These were in the form of routes.whateverPath(). We passed these method to the IndexingStatus component to generate the app-specific rotues in the shared component. In Kibana, we will not have these generators and should instead pass the path strings directly Co-authored-by: Constance --- .../indexing_status/indexing_status.test.tsx | 37 ++++-- .../indexing_status/indexing_status.tsx | 57 +++++---- .../indexing_status_fetcher.tsx | 64 ---------- .../indexing_status_logic.test.ts | 110 ++++++++++++++++++ .../indexing_status/indexing_status_logic.ts | 78 +++++++++++++ 5 files changed, 247 insertions(+), 99 deletions(-) delete mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_fetcher.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_logic.test.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_logic.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status.test.tsx index 097c3bbc8e9ff..42cb6c229ad63 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status.test.tsx @@ -4,6 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ +import '../../__mocks__/kea.mock'; +import '../../__mocks__/shallow_useeffect.mock'; + +import { setMockActions, setMockValues } from '../../__mocks__'; + import React from 'react'; import { shallow } from 'enzyme'; @@ -11,41 +16,49 @@ import { EuiPanel } from '@elastic/eui'; import { IndexingStatusContent } from './indexing_status_content'; import { IndexingStatusErrors } from './indexing_status_errors'; -import { IndexingStatusFetcher } from './indexing_status_fetcher'; import { IndexingStatus } from './indexing_status'; describe('IndexingStatus', () => { const getItemDetailPath = jest.fn(); - const getStatusPath = jest.fn(); const onComplete = jest.fn(); const setGlobalIndexingStatus = jest.fn(); + const fetchIndexingStatus = jest.fn(); const props = { percentageComplete: 50, numDocumentsWithErrors: 1, activeReindexJobId: 12, viewLinkPath: '/path', + statusPath: '/other_path', itemId: '1', getItemDetailPath, - getStatusPath, onComplete, setGlobalIndexingStatus, }; + beforeEach(() => { + setMockActions({ fetchIndexingStatus }); + }); + it('renders', () => { + setMockValues({ + percentageComplete: 50, + numDocumentsWithErrors: 0, + }); const wrapper = shallow(); - const fetcher = wrapper.find(IndexingStatusFetcher).prop('children')( - props.percentageComplete, - props.numDocumentsWithErrors - ); - expect(shallow(fetcher).find(EuiPanel)).toHaveLength(1); - expect(shallow(fetcher).find(IndexingStatusContent)).toHaveLength(1); + expect(wrapper.find(EuiPanel)).toHaveLength(1); + expect(wrapper.find(IndexingStatusContent)).toHaveLength(1); + expect(fetchIndexingStatus).toHaveBeenCalled(); }); it('renders errors', () => { - const wrapper = shallow(); - const fetcher = wrapper.find(IndexingStatusFetcher).prop('children')(100, 1); - expect(shallow(fetcher).find(IndexingStatusErrors)).toHaveLength(1); + setMockValues({ + percentageComplete: 100, + numDocumentsWithErrors: 1, + }); + const wrapper = shallow(); + + expect(wrapper.find(IndexingStatusErrors)).toHaveLength(1); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status.tsx index beec0babea590..b2109b7ef3f0b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status.tsx @@ -4,41 +4,52 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useEffect } from 'react'; + +import { useValues, useActions } from 'kea'; import { EuiPanel, EuiSpacer } from '@elastic/eui'; import { IndexingStatusContent } from './indexing_status_content'; import { IndexingStatusErrors } from './indexing_status_errors'; -import { IndexingStatusFetcher } from './indexing_status_fetcher'; +import { IndexingStatusLogic } from './indexing_status_logic'; import { IIndexingStatus } from '../types'; -export interface IIndexingStatusProps extends IIndexingStatus { +export interface IIndexingStatusProps { viewLinkPath: string; itemId: string; + statusPath: string; getItemDetailPath?(itemId: string): string; - getStatusPath(itemId: string, activeReindexJobId: number): string; onComplete(numDocumentsWithErrors: number): void; setGlobalIndexingStatus?(activeReindexJob: IIndexingStatus): void; } -export const IndexingStatus: React.FC = (props) => ( - - {(percentageComplete, numDocumentsWithErrors) => ( -
- {percentageComplete < 100 && ( - - - - )} - {percentageComplete === 100 && numDocumentsWithErrors > 0 && ( - <> - - - - )} -
- )} -
-); +export const IndexingStatus: React.FC = ({ + viewLinkPath, + statusPath, + onComplete, +}) => { + const { percentageComplete, numDocumentsWithErrors } = useValues(IndexingStatusLogic); + const { fetchIndexingStatus } = useActions(IndexingStatusLogic); + + useEffect(() => { + fetchIndexingStatus({ statusPath, onComplete }); + }, []); + + return ( + <> + {percentageComplete < 100 && ( + + + + )} + {percentageComplete === 100 && numDocumentsWithErrors > 0 && ( + <> + + + + )} + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_fetcher.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_fetcher.tsx deleted file mode 100644 index cb7c82f91ed61..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_fetcher.tsx +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { useEffect, useState, useRef } from 'react'; - -import { HttpLogic } from '../http'; -import { flashAPIErrors } from '../flash_messages'; - -interface IIndexingStatusFetcherProps { - activeReindexJobId: number; - itemId: string; - percentageComplete: number; - numDocumentsWithErrors: number; - onComplete?(numDocumentsWithErrors: number): void; - getStatusPath(itemId: string, activeReindexJobId: number): string; - children(percentageComplete: number, numDocumentsWithErrors: number): JSX.Element; -} - -export const IndexingStatusFetcher: React.FC = ({ - activeReindexJobId, - children, - getStatusPath, - itemId, - numDocumentsWithErrors, - onComplete, - percentageComplete = 0, -}) => { - const [indexingStatus, setIndexingStatus] = useState({ - numDocumentsWithErrors, - percentageComplete, - }); - const pollingInterval = useRef(); - - useEffect(() => { - pollingInterval.current = window.setInterval(async () => { - try { - const response = await HttpLogic.values.http.get(getStatusPath(itemId, activeReindexJobId)); - if (response.percentageComplete >= 100) { - clearInterval(pollingInterval.current); - } - setIndexingStatus({ - percentageComplete: response.percentageComplete, - numDocumentsWithErrors: response.numDocumentsWithErrors, - }); - if (response.percentageComplete >= 100 && onComplete) { - onComplete(response.numDocumentsWithErrors); - } - } catch (e) { - flashAPIErrors(e); - } - }, 3000); - - return () => { - if (pollingInterval.current) { - clearInterval(pollingInterval.current); - } - }; - }, []); - - return children(indexingStatus.percentageComplete, indexingStatus.numDocumentsWithErrors); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_logic.test.ts new file mode 100644 index 0000000000000..9fa5fe0f84bab --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_logic.test.ts @@ -0,0 +1,110 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { resetContext } from 'kea'; + +jest.mock('../http', () => ({ + HttpLogic: { + values: { http: { get: jest.fn() } }, + }, +})); +import { HttpLogic } from '../http'; + +jest.mock('../flash_messages', () => ({ + flashAPIErrors: jest.fn(), +})); +import { flashAPIErrors } from '../flash_messages'; + +import { IndexingStatusLogic } from './indexing_status_logic'; + +describe('IndexingStatusLogic', () => { + let unmount: any; + + const mockStatusResponse = { + percentageComplete: 50, + numDocumentsWithErrors: 3, + activeReindexJobId: 1, + }; + + beforeEach(() => { + jest.clearAllMocks(); + resetContext({}); + unmount = IndexingStatusLogic.mount(); + }); + + it('has expected default values', () => { + expect(IndexingStatusLogic.values).toEqual({ + percentageComplete: 100, + numDocumentsWithErrors: 0, + }); + }); + + describe('setIndexingStatus', () => { + it('sets reducers', () => { + IndexingStatusLogic.actions.setIndexingStatus(mockStatusResponse); + + expect(IndexingStatusLogic.values.percentageComplete).toEqual( + mockStatusResponse.percentageComplete + ); + expect(IndexingStatusLogic.values.numDocumentsWithErrors).toEqual( + mockStatusResponse.numDocumentsWithErrors + ); + }); + }); + + describe('fetchIndexingStatus', () => { + jest.useFakeTimers(); + const statusPath = '/api/workplace_search/path/123'; + const onComplete = jest.fn(); + const TIMEOUT = 3000; + + it('calls API and sets values', async () => { + const setIndexingStatusSpy = jest.spyOn(IndexingStatusLogic.actions, 'setIndexingStatus'); + const promise = Promise.resolve(mockStatusResponse); + (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + + IndexingStatusLogic.actions.fetchIndexingStatus({ statusPath, onComplete }); + jest.advanceTimersByTime(TIMEOUT); + + expect(HttpLogic.values.http.get).toHaveBeenCalledWith(statusPath); + await promise; + + expect(setIndexingStatusSpy).toHaveBeenCalledWith(mockStatusResponse); + }); + + it('handles error', async () => { + const promise = Promise.reject('An error occured'); + (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + + IndexingStatusLogic.actions.fetchIndexingStatus({ statusPath, onComplete }); + jest.advanceTimersByTime(TIMEOUT); + + try { + await promise; + } catch { + // Do nothing + } + expect(flashAPIErrors).toHaveBeenCalledWith('An error occured'); + }); + + it('handles indexing complete state', async () => { + const promise = Promise.resolve({ ...mockStatusResponse, percentageComplete: 100 }); + (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + IndexingStatusLogic.actions.fetchIndexingStatus({ statusPath, onComplete }); + jest.advanceTimersByTime(TIMEOUT); + + await promise; + + expect(clearInterval).toHaveBeenCalled(); + expect(onComplete).toHaveBeenCalledWith(mockStatusResponse.numDocumentsWithErrors); + }); + + it('handles unmounting', async () => { + unmount(); + expect(clearInterval).toHaveBeenCalled(); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_logic.ts b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_logic.ts new file mode 100644 index 0000000000000..cb484f19c157f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_logic.ts @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { kea, MakeLogicType } from 'kea'; + +import { HttpLogic } from '../http'; +import { IIndexingStatus } from '../types'; +import { flashAPIErrors } from '../flash_messages'; + +interface IndexingStatusProps { + statusPath: string; + onComplete(numDocumentsWithErrors: number): void; +} + +interface IndexingStatusActions { + fetchIndexingStatus(props: IndexingStatusProps): IndexingStatusProps; + setIndexingStatus({ + percentageComplete, + numDocumentsWithErrors, + }: IIndexingStatus): IIndexingStatus; +} + +interface IndexingStatusValues { + percentageComplete: number; + numDocumentsWithErrors: number; +} + +let pollingInterval: number; + +export const IndexingStatusLogic = kea>({ + actions: { + fetchIndexingStatus: ({ statusPath, onComplete }) => ({ statusPath, onComplete }), + setIndexingStatus: ({ numDocumentsWithErrors, percentageComplete }) => ({ + numDocumentsWithErrors, + percentageComplete, + }), + }, + reducers: { + percentageComplete: [ + 100, + { + setIndexingStatus: (_, { percentageComplete }) => percentageComplete, + }, + ], + numDocumentsWithErrors: [ + 0, + { + setIndexingStatus: (_, { numDocumentsWithErrors }) => numDocumentsWithErrors, + }, + ], + }, + listeners: ({ actions }) => ({ + fetchIndexingStatus: ({ statusPath, onComplete }: IndexingStatusProps) => { + const { http } = HttpLogic.values; + + pollingInterval = window.setInterval(async () => { + try { + const response: IIndexingStatus = await http.get(statusPath); + if (response.percentageComplete >= 100) { + clearInterval(pollingInterval); + onComplete(response.numDocumentsWithErrors); + } + actions.setIndexingStatus(response); + } catch (e) { + flashAPIErrors(e); + } + }, 3000); + }, + }), + events: () => ({ + beforeUnmount() { + clearInterval(pollingInterval); + }, + }), +}); From 0e43beed4ffb5a2e0e0d5826f79b0b510be3364e Mon Sep 17 00:00:00 2001 From: Quynh Nguyen <43350163+qn895@users.noreply.github.com> Date: Wed, 2 Dec 2020 13:35:38 -0600 Subject: [PATCH 17/40] [ML] Fix prediction probability text for classification (#84593) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../decision_path_chart.tsx | 31 ++++++++++++------- .../translations/translations/ja-JP.json | 2 -- .../translations/translations/zh-CN.json | 2 -- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/ml/public/application/components/data_grid/feature_importance/decision_path_chart.tsx b/x-pack/plugins/ml/public/application/components/data_grid/feature_importance/decision_path_chart.tsx index 6bfd7a66331df..38eb7abc16814 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/feature_importance/decision_path_chart.tsx +++ b/x-pack/plugins/ml/public/application/components/data_grid/feature_importance/decision_path_chart.tsx @@ -91,8 +91,7 @@ export const DecisionPathChart = ({ maxDomain, baseline, }: DecisionPathChartProps) => { - // adjust the height so it's compact for items with more features - const baselineData: LineAnnotationDatum[] | undefined = useMemo( + const regressionBaselineData: LineAnnotationDatum[] | undefined = useMemo( () => baseline && isRegressionFeatureImportanceBaseline(baseline) ? [ @@ -111,6 +110,19 @@ export const DecisionPathChart = ({ : undefined, [baseline] ); + const xAxisLabel = regressionBaselineData + ? i18n.translate( + 'xpack.ml.dataframe.analytics.explorationResults.decisionPathLinePredictionTitle', + { + defaultMessage: 'Prediction', + } + ) + : i18n.translate( + 'xpack.ml.dataframe.analytics.explorationResults.decisionPathLinePredictionProbabilityTitle', + { + defaultMessage: 'Prediction probability', + } + ); // if regression, guarantee up to num_precision significant digits without having it in scientific notation // if classification, hide the numeric values since we only want to show the path const tickFormatter = useCallback((d) => formatSingleValue(d, '').toString(), []); @@ -121,11 +133,11 @@ export const DecisionPathChart = ({ size={{ height: DECISION_PATH_MARGIN + decisionPathData.length * DECISION_PATH_ROW_HEIGHT }} > - {baselineData && ( + {regressionBaselineData && ( @@ -137,8 +149,8 @@ export const DecisionPathChart = ({ title={i18n.translate( 'xpack.ml.dataframe.analytics.explorationResults.decisionPathXAxisTitle', { - defaultMessage: "Prediction for '{predictionFieldName}'", - values: { predictionFieldName }, + defaultMessage: "{xAxisLabel} for '{predictionFieldName}'", + values: { predictionFieldName, xAxisLabel }, } )} showGridLines={false} @@ -156,12 +168,7 @@ export const DecisionPathChart = ({ Date: Wed, 2 Dec 2020 14:37:36 -0500 Subject: [PATCH 18/40] [SECURITY_SOLUTION] Enable usage of the Endpoint Policy form from Fleet (#84684) * Endpoint: add `withSecurityContext` HOC + refactor endpoint policy edit lazy component to use it * Endpoint: refactor Policy Details to separate form from view * Endpoint: Enable the Redux store for the Policy form when displayed via Fleet * Fleet: Allow partial package policy updates to be sent via `onChange()` --- .../step_define_package_policy.tsx | 1 + .../edit_package_policy_page/index.tsx | 2 + .../applications/fleet/types/ui_extensions.ts | 8 +- .../view/components/config_form/index.tsx | 4 +- .../endpoint_policy_edit_extension.tsx | 102 +++++++++++++----- .../lazy_endpoint_policy_edit_extension.tsx | 31 ++++-- .../with_security_context.tsx | 64 +++++++++++ .../pages/policy/view/policy_details.tsx | 60 ++--------- .../pages/policy/view/policy_details_form.tsx | 68 ++++++++++++ .../security_solution/public/plugin.tsx | 4 +- .../apps/endpoint/policy_details.ts | 70 ++++++++---- .../apps/endpoint/policy_list.ts | 2 +- ...gest_manager_create_package_policy_page.ts | 29 +++-- 13 files changed, 323 insertions(+), 122 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/with_security_context.tsx create mode 100644 x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details_form.tsx diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_define_package_policy.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_define_package_policy.tsx index 9f687b39c094e..f6533a06cea27 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_define_package_policy.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_define_package_policy.tsx @@ -143,6 +143,7 @@ export const StepDefinePackagePolicy: React.FunctionComponent<{ description: e.target.value, }) } + data-test-subj="packagePolicyDescriptionInput" /> diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx index 425d1435a716e..8f798445b2362 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx @@ -244,6 +244,7 @@ export const EditPackagePolicyPage: React.FunctionComponent = () => { packagePolicyName: packagePolicy.name, }, }), + 'data-test-subj': 'policyUpdateSuccessToast', text: agentCount && agentPolicy ? i18n.translate('xpack.fleet.editPackagePolicy.updatedNotificationMessage', { @@ -406,6 +407,7 @@ export const EditPackagePolicyPage: React.FunctionComponent = () => { iconType="save" color="primary" fill + data-test-subj="saveIntegration" > ; }) => void; } diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/components/config_form/index.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/components/config_form/index.tsx index 30c35de9b907f..ce5eb03d60cd0 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/components/config_form/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/components/config_form/index.tsx @@ -58,12 +58,12 @@ export const ConfigForm: FC = memo( {TITLES.type} {type} - + {TITLES.os} {supportedOss.map((os) => OS_TITLES[os]).join(', ')} - + {rightCorner} diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_edit_extension.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_edit_extension.tsx index 95bf23b532f41..6d464280b2763 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_edit_extension.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_edit_extension.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { memo, useCallback, useMemo, useState } from 'react'; +import React, { memo, useCallback, useEffect, useMemo, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiCallOut, @@ -19,9 +19,11 @@ import { EuiContextMenuPanelProps, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { useDispatch } from 'react-redux'; import { pagePathGetters, PackagePolicyEditExtensionComponentProps, + NewPackagePolicy, } from '../../../../../../../fleet/public'; import { getPolicyDetailPath, getTrustedAppsListPath } from '../../../../common/routing'; import { MANAGEMENT_APP_ID } from '../../../../common/constants'; @@ -31,13 +33,17 @@ import { } from '../../../../../../common/endpoint/types'; import { useKibana } from '../../../../../common/lib/kibana'; import { useNavigateToAppEventHandler } from '../../../../../common/hooks/endpoint/use_navigate_to_app_event_handler'; +import { PolicyDetailsForm } from '../policy_details_form'; +import { AppAction } from '../../../../../common/store/actions'; +import { usePolicyDetailsSelector } from '../policy_hooks'; +import { policyDetailsForUpdate } from '../../store/policy_details/selectors'; /** * Exports Endpoint-specific package policy instructions * for use in the Ingest app create / edit package policy */ export const EndpointPolicyEditExtension = memo( - ({ policy }) => { + ({ policy, onChange }) => { return ( <> @@ -46,12 +52,81 @@ export const EndpointPolicyEditExtension = memo + + ); } ); EndpointPolicyEditExtension.displayName = 'EndpointPolicyEditExtension'; +const WrappedPolicyDetailsForm = memo<{ + policyId: string; + onChange: PackagePolicyEditExtensionComponentProps['onChange']; +}>(({ policyId, onChange }) => { + const dispatch = useDispatch<(a: AppAction) => void>(); + const updatedPolicy = usePolicyDetailsSelector(policyDetailsForUpdate); + const [, setLastUpdatedPolicy] = useState(updatedPolicy); + + // When the form is initially displayed, trigger the Redux middleware which is based on + // the location information stored via the `userChangedUrl` action. + useEffect(() => { + dispatch({ + type: 'userChangedUrl', + payload: { + hash: '', + pathname: getPolicyDetailPath(policyId, ''), + search: '', + }, + }); + + // When form is unloaded, reset the redux store + return () => { + dispatch({ + type: 'userChangedUrl', + payload: { + hash: '', + pathname: '/', + search: '', + }, + }); + }; + }, [dispatch, policyId]); + + useEffect(() => { + // Currently, the `onChange` callback provided by the fleet UI extension is regenerated every + // time the policy data is updated, which means this will go into a continious loop if we don't + // actually check to see if an update should be reported back to fleet + setLastUpdatedPolicy((prevState) => { + if (prevState === updatedPolicy) { + return prevState; + } + + if (updatedPolicy) { + onChange({ + isValid: true, + // send up only the updated policy data which is stored in the `inputs` section. + // All other attributes (like name, id) are updated from the Fleet form, so we want to + // ensure we don't override it. + updatedPolicy: { + // Casting is needed due to the use of `Immutable<>` in our store data + inputs: (updatedPolicy.inputs as unknown) as NewPackagePolicy['inputs'], + }, + }); + } + + return updatedPolicy; + }); + }, [onChange, updatedPolicy]); + + return ( +
+ +
+ ); +}); +WrappedPolicyDetailsForm.displayName = 'WrappedPolicyDetailsForm'; + const EditFlowMessage = memo<{ agentPolicyId: string; integrationPolicyId: string; @@ -82,17 +157,6 @@ const EditFlowMessage = memo<{ const handleClosePopup = useCallback(() => setIsMenuOpen(false), []); - const handleSecurityPolicyAction = useNavigateToAppEventHandler( - MANAGEMENT_APP_ID, - { - path: getPolicyDetailPath(integrationPolicyId), - state: { - onSaveNavigateTo: navigateBackToIngest, - onCancelNavigateTo: navigateBackToIngest, - }, - } - ); - const handleTrustedAppsAction = useNavigateToAppEventHandler( MANAGEMENT_APP_ID, { @@ -129,16 +193,6 @@ const EditFlowMessage = memo<{ const actionItems = useMemo(() => { return [ - - - , , ]; - }, [handleSecurityPolicyAction, handleTrustedAppsAction]); + }, [handleTrustedAppsAction]); return ( diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_edit_extension.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_edit_extension.tsx index b417bc9ad5d9c..78a83fa11ae3f 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_edit_extension.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_edit_extension.tsx @@ -5,14 +5,29 @@ */ import { lazy } from 'react'; -import { PackagePolicyEditExtensionComponent } from '../../../../../../../fleet/public'; +import { CoreStart } from 'kibana/public'; +import { + PackagePolicyEditExtensionComponent, + PackagePolicyEditExtensionComponentProps, +} from '../../../../../../../fleet/public'; +import { StartPlugins } from '../../../../../types'; + +export const getLazyEndpointPolicyEditExtension = ( + coreStart: CoreStart, + depsStart: Pick +) => { + return lazy(async () => { + const [{ withSecurityContext }, { EndpointPolicyEditExtension }] = await Promise.all([ + import('./with_security_context'), + import('./endpoint_policy_edit_extension'), + ]); -export const LazyEndpointPolicyEditExtension = lazy( - async () => { - const { EndpointPolicyEditExtension } = await import('./endpoint_policy_edit_extension'); return { - // FIXME: remove casting once old UI component registration is removed - default: (EndpointPolicyEditExtension as unknown) as PackagePolicyEditExtensionComponent, + default: withSecurityContext({ + coreStart, + depsStart, + WrappedComponent: EndpointPolicyEditExtension, + }), }; - } -); + }); +}; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/with_security_context.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/with_security_context.tsx new file mode 100644 index 0000000000000..f65dbaf1087d8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/with_security_context.tsx @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { ComponentType, memo } from 'react'; +import { CoreStart } from 'kibana/public'; +import { combineReducers, createStore, compose, applyMiddleware } from 'redux'; +import { Provider as ReduxStoreProvider } from 'react-redux'; +import { StartPlugins } from '../../../../../types'; +import { managementReducer } from '../../../../store/reducer'; +import { managementMiddlewareFactory } from '../../../../store/middleware'; + +type ComposeType = typeof compose; +declare global { + interface Window { + __REDUX_DEVTOOLS_EXTENSION_COMPOSE__: ComposeType; + } +} +const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; + +interface WithSecurityContextProps

{ + coreStart: CoreStart; + depsStart: Pick; + WrappedComponent: ComponentType

; +} + +/** + * Returns a new component that wraps the provided `WrappedComponent` in a bare minimum set of rendering context + * needed to render Security Solution components that may be dependent on a Redux store and/or Security Solution + * specific context based functionality + * + * @param coreStart + * @param depsStart + * @param WrappedComponent + */ +export const withSecurityContext =

({ + coreStart, + depsStart, + WrappedComponent, +}: WithSecurityContextProps

): ComponentType

=> { + let store: ReturnType; // created on first render + + return memo((props) => { + if (!store) { + // Most of the code here was copied form + // x-pack/plugins/security_solution/public/management/index.ts + store = createStore( + combineReducers({ + management: managementReducer, + }), + { management: undefined }, + composeEnhancers(applyMiddleware(...managementMiddlewareFactory(coreStart, depsStart))) + ); + } + + return ( + + + + ); + }); +}; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx index 1ce099c494cf0..666e27c9d9a26 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx @@ -10,7 +10,6 @@ import { EuiFlexItem, EuiButton, EuiButtonEmpty, - EuiText, EuiSpacer, EuiOverlayMask, EuiConfirmModal, @@ -34,9 +33,6 @@ import { import { useKibana, toMountPoint } from '../../../../../../../../src/plugins/kibana_react/public'; import { AgentsSummary } from './agents_summary'; import { VerticalDivider } from './vertical_divider'; -import { WindowsEvents, MacEvents, LinuxEvents } from './policy_forms/events'; -import { MalwareProtections } from './policy_forms/protections/malware'; -import { AntivirusRegistrationForm } from './components/antivirus_registration_form'; import { useToasts } from '../../../../common/lib/kibana'; import { AppAction } from '../../../../common/store/actions'; import { SpyRoute } from '../../../../common/utils/route/spy_routes'; @@ -48,7 +44,7 @@ import { MANAGEMENT_APP_ID } from '../../../common/constants'; import { PolicyDetailsRouteState } from '../../../../../common/endpoint/types'; import { WrapperPage } from '../../../../common/components/wrapper_page'; import { HeaderPage } from '../../../../common/components/header_page'; -import { AdvancedPolicyForms } from './policy_advanced'; +import { PolicyDetailsForm } from './policy_details_form'; export const PolicyDetails = React.memo(() => { const dispatch = useDispatch<(action: AppAction) => void>(); @@ -71,7 +67,6 @@ export const PolicyDetails = React.memo(() => { // Local state const [showConfirm, setShowConfirm] = useState(false); const [routeState, setRouteState] = useState(); - const [showAdvancedPolicy, setShowAdvancedPolicy] = useState(false); const policyName = policyItem?.name ?? ''; const hostListRouterPath = getEndpointListPath({ name: 'endpointList' }); @@ -111,9 +106,11 @@ export const PolicyDetails = React.memo(() => { } }, [navigateToApp, toasts, policyName, policyUpdateStatus, routeState]); + const routingOnCancelNavigateTo = routeState?.onCancelNavigateTo; const navigateToAppArguments = useMemo((): Parameters => { - return routeState?.onCancelNavigateTo ?? [MANAGEMENT_APP_ID, { path: hostListRouterPath }]; - }, [hostListRouterPath, routeState?.onCancelNavigateTo]); + return routingOnCancelNavigateTo ?? [MANAGEMENT_APP_ID, { path: hostListRouterPath }]; + }, [hostListRouterPath, routingOnCancelNavigateTo]); + const handleCancelOnClick = useNavigateToAppEventHandler(...navigateToAppArguments); const handleSaveOnClick = useCallback(() => { @@ -131,10 +128,6 @@ export const PolicyDetails = React.memo(() => { setShowConfirm(false); }, []); - const handleAdvancedPolicyClick = useCallback(() => { - setShowAdvancedPolicy(!showAdvancedPolicy); - }, [showAdvancedPolicy]); - useEffect(() => { if (!routeState && locationRouteState) { setRouteState(locationRouteState); @@ -224,48 +217,7 @@ export const PolicyDetails = React.memo(() => { {headerRightContent} - -

- -

- - - - - - - -

- -

-
- - - - - - - - - - - - - - - - - {showAdvancedPolicy && } + diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details_form.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details_form.tsx new file mode 100644 index 0000000000000..a0bf2b37e8a12 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details_form.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiButtonEmpty, EuiSpacer, EuiText } from '@elastic/eui'; +import React, { memo, useCallback, useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { MalwareProtections } from './policy_forms/protections/malware'; +import { LinuxEvents, MacEvents, WindowsEvents } from './policy_forms/events'; +import { AdvancedPolicyForms } from './policy_advanced'; +import { AntivirusRegistrationForm } from './components/antivirus_registration_form'; + +export const PolicyDetailsForm = memo(() => { + const [showAdvancedPolicy, setShowAdvancedPolicy] = useState(false); + const handleAdvancedPolicyClick = useCallback(() => { + setShowAdvancedPolicy(!showAdvancedPolicy); + }, [showAdvancedPolicy]); + + return ( + <> + +

+ +

+
+ + + + + + +

+ +

+
+ + + + + + + + + + + + + + + + + {showAdvancedPolicy && } + + ); +}); +PolicyDetailsForm.displayName = 'PolicyDetailsForm'; diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index b81be3249953e..4f37b5b15d73a 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -61,7 +61,7 @@ import { import { SecurityAppStore } from './common/store/store'; import { getCaseConnectorUI } from './cases/components/connectors'; import { licenseService } from './common/hooks/use_license'; -import { LazyEndpointPolicyEditExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_edit_extension'; +import { getLazyEndpointPolicyEditExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_edit_extension'; import { LazyEndpointPolicyCreateExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_create_extension'; export class Plugin implements IPlugin { @@ -337,7 +337,7 @@ export class Plugin implements IPlugin('button'); - const expectedItems = ['Edit Policy', 'Edit Trusted Applications']; + const expectedItems = ['Edit Trusted Applications']; for (const action of actionItems) { const buttonText = await action.getVisibleText(); @@ -289,27 +289,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { } }); - it('should navigate to Policy Details when the edit security policy action is clicked', async () => { - await pageObjects.ingestManagerCreatePackagePolicy.selectEndpointAction('policy'); - await pageObjects.policy.ensureIsOnDetailsPage(); - }); - - it('should allow the user to navigate, edit, save Policy Details and be redirected back to ingest', async () => { - await pageObjects.ingestManagerCreatePackagePolicy.selectEndpointAction('policy'); - await pageObjects.policy.ensureIsOnDetailsPage(); - await pageObjects.endpointPageUtils.clickOnEuiCheckbox('policyWindowsEvent_dns'); - await pageObjects.policy.confirmAndSave(); - - await testSubjects.existOrFail('policyDetailsSuccessMessage'); - await pageObjects.ingestManagerCreatePackagePolicy.ensureOnEditPageOrFail(); - }); - - it('should navigate back to Ingest Policy Edit package page on click of cancel button', async () => { - await pageObjects.ingestManagerCreatePackagePolicy.selectEndpointAction('policy'); - await (await pageObjects.policy.findCancelButton()).click(); - await pageObjects.ingestManagerCreatePackagePolicy.ensureOnEditPageOrFail(); - }); - it('should navigate to Trusted Apps', async () => { await pageObjects.ingestManagerCreatePackagePolicy.selectEndpointAction('trustedApps'); await pageObjects.trustedApps.ensureIsOnTrustedAppsListPage(); @@ -321,6 +300,53 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await backButton.click(); await pageObjects.ingestManagerCreatePackagePolicy.ensureOnEditPageOrFail(); }); + + it('should show the endpoint policy form', async () => { + await testSubjects.existOrFail('endpointIntegrationPolicyForm'); + }); + + it('should allow updates to policy items', async () => { + const winDnsEventingCheckbox = await testSubjects.find('policyWindowsEvent_dns'); + await pageObjects.ingestManagerCreatePackagePolicy.scrollToCenterOfWindow( + winDnsEventingCheckbox + ); + expect(await winDnsEventingCheckbox.isSelected()).to.be(true); + await pageObjects.endpointPageUtils.clickOnEuiCheckbox('policyWindowsEvent_dns'); + expect(await winDnsEventingCheckbox.isSelected()).to.be(false); + }); + + it('should preserve updates done from the Fleet form', async () => { + await pageObjects.ingestManagerCreatePackagePolicy.setPackagePolicyDescription( + 'protect everything' + ); + + const winDnsEventingCheckbox = await testSubjects.find('policyWindowsEvent_dns'); + await pageObjects.ingestManagerCreatePackagePolicy.scrollToCenterOfWindow( + winDnsEventingCheckbox + ); + await pageObjects.endpointPageUtils.clickOnEuiCheckbox('policyWindowsEvent_dns'); + + expect( + await pageObjects.ingestManagerCreatePackagePolicy.getPackagePolicyDescriptionValue() + ).to.be('protect everything'); + }); + + it('should include updated endpoint data when saved', async () => { + const winDnsEventingCheckbox = await testSubjects.find('policyWindowsEvent_dns'); + await pageObjects.ingestManagerCreatePackagePolicy.scrollToCenterOfWindow( + winDnsEventingCheckbox + ); + await pageObjects.endpointPageUtils.clickOnEuiCheckbox('policyWindowsEvent_dns'); + const wasSelected = await winDnsEventingCheckbox.isSelected(); + await (await pageObjects.ingestManagerCreatePackagePolicy.findSaveButton(true)).click(); + await pageObjects.ingestManagerCreatePackagePolicy.waitForSaveSuccessNotification(true); + + await pageObjects.ingestManagerCreatePackagePolicy.navigateToAgentPolicyEditPackagePolicy( + policyInfo.agentPolicy.id, + policyInfo.packagePolicy.id + ); + expect(await testSubjects.isSelected('policyWindowsEvent_dns')).to.be(wasSelected); + }); }); }); } diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_list.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_list.ts index 70958d7ca7631..741040b12fd7b 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_list.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_list.ts @@ -140,7 +140,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const newPolicyName = `endpoint policy ${Date.now()}`; await pageObjects.ingestManagerCreatePackagePolicy.selectAgentPolicy(); await pageObjects.ingestManagerCreatePackagePolicy.setPackagePolicyName(newPolicyName); - await (await pageObjects.ingestManagerCreatePackagePolicy.findDSaveButton()).click(); + await (await pageObjects.ingestManagerCreatePackagePolicy.findSaveButton()).click(); await pageObjects.ingestManagerCreatePackagePolicy.waitForSaveSuccessNotification(); await pageObjects.policy.ensureIsOnPolicyPage(); await policyTestResources.deletePolicyByName(newPolicyName); diff --git a/x-pack/test/security_solution_endpoint/page_objects/ingest_manager_create_package_policy_page.ts b/x-pack/test/security_solution_endpoint/page_objects/ingest_manager_create_package_policy_page.ts index 747b62a9550c6..48e5b6a23458f 100644 --- a/x-pack/test/security_solution_endpoint/page_objects/ingest_manager_create_package_policy_page.ts +++ b/x-pack/test/security_solution_endpoint/page_objects/ingest_manager_create_package_policy_page.ts @@ -41,8 +41,10 @@ export function IngestManagerCreatePackagePolicy({ /** * Finds and returns the save button on the sticky bottom bar */ - async findDSaveButton() { - return await testSubjects.find('createPackagePolicySaveButton'); + async findSaveButton(forEditPage: boolean = false) { + return await testSubjects.find( + forEditPage ? 'saveIntegration' : 'createPackagePolicySaveButton' + ); }, /** @@ -80,11 +82,22 @@ export function IngestManagerCreatePackagePolicy({ await testSubjects.setValue('packagePolicyNameInput', name); }, + async getPackagePolicyDescriptionValue() { + return await testSubjects.getAttribute('packagePolicyDescriptionInput', 'value'); + }, + + async setPackagePolicyDescription(desc: string) { + await this.scrollToCenterOfWindow('packagePolicyDescriptionInput'); + await testSubjects.setValue('packagePolicyDescriptionInput', desc); + }, + /** * Waits for the save Notification toast to be visible */ - async waitForSaveSuccessNotification() { - await testSubjects.existOrFail('packagePolicyCreateSuccessToast'); + async waitForSaveSuccessNotification(forEditPage: boolean = false) { + await testSubjects.existOrFail( + forEditPage ? 'policyUpdateSuccessToast' : 'packagePolicyCreateSuccessToast' + ); }, /** @@ -115,11 +128,13 @@ export function IngestManagerCreatePackagePolicy({ /** * Center a given Element on the Window viewport - * @param element + * @param element if defined as a string, it should be the test subject to find */ - async scrollToCenterOfWindow(element: WebElementWrapper) { + async scrollToCenterOfWindow(element: WebElementWrapper | string) { + const ele = typeof element === 'string' ? await testSubjects.find(element) : element; + const [elementPosition, windowSize] = await Promise.all([ - element.getPosition(), + ele.getPosition(), browser.getWindowSize(), ]); await browser.execute( From b593781009c47b4f5cf8d5010d571d55f6b2dcbc Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Wed, 2 Dec 2020 11:42:23 -0800 Subject: [PATCH 19/40] Jest multi-project configuration (#77894) Signed-off-by: Tyler Smalley --- .../development-unit-tests.asciidoc | 6 +- ...tegration.js => jest.config.integration.js | 16 +- jest.config.js | 23 + jest.config.oss.js | 30 + packages/README.md | 2 +- packages/kbn-apm-config-loader/jest.config.js | 24 + packages/kbn-babel-code-parser/jest.config.js | 24 + packages/kbn-config-schema/jest.config.js | 24 + packages/kbn-config/jest.config.js | 24 + packages/kbn-dev-utils/jest.config.js | 24 + packages/kbn-es-archiver/jest.config.js | 24 + packages/kbn-es/jest.config.js | 24 + packages/kbn-i18n/jest.config.js | 25 + packages/kbn-interpreter/jest.config.js | 24 + packages/kbn-legacy-logging/jest.config.js | 24 + packages/kbn-logging/jest.config.js | 24 + packages/kbn-monaco/jest.config.js | 24 + packages/kbn-optimizer/jest.config.js | 24 + packages/kbn-plugin-generator/jest.config.js | 24 + packages/kbn-pm/jest.config.js | 24 + packages/kbn-release-notes/jest.config.js | 24 + packages/kbn-spec-to-console/jest.config.js | 24 + packages/kbn-std/jest.config.js | 24 + packages/kbn-telemetry-tools/jest.config.js | 24 + packages/kbn-test/jest.config.js | 24 + packages/kbn-test/src/index.ts | 2 + .../src/jest/run_check_jest_configs_cli.ts | 113 + packages/kbn-ui-framework/jest.config.js | 24 + packages/kbn-utils/jest.config.js | 24 + .../cli.js => scripts/check_jest_configs.js | 5 +- scripts/jest.js | 15 +- scripts/jest_integration.js | 16 +- src/cli/jest.config.js | 24 + src/cli_encryption_keys/jest.config.js | 24 + src/cli_keystore/jest.config.js | 24 + src/cli_plugin/jest.config.js | 24 + src/core/jest.config.js | 25 + .../integration_tests/team_assignment.test.js | 3 +- src/dev/jest.config.js | 24 + src/legacy/server/jest.config.js | 24 + src/legacy/ui/jest.config.js | 24 + src/legacy/utils/jest.config.js | 24 + src/optimize/jest.config.js | 24 + src/plugins/advanced_settings/jest.config.js | 24 + src/plugins/bfetch/jest.config.js | 24 + src/plugins/charts/jest.config.js | 24 + .../console/jest.config.js} | 21 +- src/plugins/dashboard/jest.config.js | 25 + src/plugins/data/jest.config.js | 25 + src/plugins/discover/jest.config.js | 25 + src/plugins/embeddable/jest.config.js | 25 + src/plugins/es_ui_shared/jest.config.js | 24 + src/plugins/expressions/jest.config.js | 24 + src/plugins/home/jest.config.js | 24 + .../index_pattern_management/jest.config.js | 25 + src/plugins/input_control_vis/jest.config.js | 24 + src/plugins/inspector/jest.config.js | 24 + src/plugins/kibana_legacy/jest.config.js | 24 + src/plugins/kibana_overview/jest.config.js | 24 + src/plugins/kibana_react/jest.config.js | 24 + .../kibana_usage_collection/jest.config.js | 24 + src/plugins/kibana_utils/jest.config.js | 24 + src/plugins/legacy_export/jest.config.js | 24 + src/plugins/management/jest.config.js | 24 + src/plugins/maps_legacy/jest.config.js | 24 + src/plugins/navigation/jest.config.js | 24 + src/plugins/newsfeed/jest.config.js | 24 + src/plugins/region_map/jest.config.js | 24 + src/plugins/saved_objects/jest.config.js | 24 + .../saved_objects_management/jest.config.js | 24 + .../saved_objects_tagging_oss/jest.config.js | 24 + src/plugins/security_oss/jest.config.js | 24 + src/plugins/share/jest.config.js | 24 + src/plugins/telemetry/jest.config.js | 24 + .../jest.config.js | 24 + .../jest.config.js | 24 + src/plugins/tile_map/jest.config.js | 24 + src/plugins/ui_actions/jest.config.js | 24 + src/plugins/url_forwarding/jest.config.js | 24 + src/plugins/usage_collection/jest.config.js | 24 + src/plugins/vis_default_editor/jest.config.js | 24 + src/plugins/vis_type_markdown/jest.config.js | 24 + src/plugins/vis_type_metric/jest.config.js | 24 + src/plugins/vis_type_table/jest.config.js | 25 + src/plugins/vis_type_tagcloud/jest.config.js | 25 + src/plugins/vis_type_timelion/jest.config.js | 24 + .../vis_type_timeseries/jest.config.js | 24 + src/plugins/vis_type_vega/jest.config.js | 24 + src/plugins/vis_type_vislib/jest.config.js | 24 + src/plugins/visualizations/jest.config.js | 24 + src/plugins/visualize/jest.config.js | 24 + src/setup_node_env/jest.config.js | 24 + src/test_utils/jest.config.js | 24 + test/functional/jest.config.js | 24 + test/scripts/checks/jest_configs.sh | 5 + vars/tasks.groovy | 1 + x-pack/dev-tools/jest/create_jest_config.js | 22 - x-pack/dev-tools/jest/index.js | 25 - x-pack/jest.config.js | 14 + x-pack/plugins/actions/jest.config.js | 11 + .../plugins/alerting_builtins/jest.config.js | 11 + x-pack/plugins/alerts/jest.config.js | 11 + x-pack/plugins/apm/jest.config.js | 32 +- x-pack/plugins/audit_trail/jest.config.js | 11 + .../plugins/beats_management/jest.config.js | 11 + x-pack/plugins/canvas/jest.config.js | 11 + x-pack/plugins/canvas/scripts/jest.js | 108 +- .../plugins/canvas/storybook/dll_contexts.js | 3 - x-pack/plugins/case/jest.config.js | 11 + x-pack/plugins/cloud/jest.config.js | 11 + x-pack/plugins/code/jest.config.js | 11 + .../cross_cluster_replication/jest.config.js | 11 + .../plugins/dashboard_enhanced/jest.config.js | 11 + x-pack/plugins/dashboard_mode/jest.config.js | 11 + x-pack/plugins/data_enhanced/jest.config.js | 11 + .../plugins/discover_enhanced/jest.config.js | 11 + x-pack/plugins/drilldowns/jest.config.js | 11 + .../embeddable_enhanced/jest.config.js | 11 + .../encrypted_saved_objects/jest.config.js | 11 + .../plugins/enterprise_search/jest.config.js | 11 + x-pack/plugins/event_log/jest.config.js | 11 + x-pack/plugins/features/jest.config.js | 11 + x-pack/plugins/file_upload/jest.config.js | 11 + x-pack/plugins/fleet/jest.config.js | 11 + x-pack/plugins/global_search/jest.config.js | 11 + .../plugins/global_search_bar/jest.config.js | 11 + .../global_search_providers/jest.config.js | 11 + x-pack/plugins/graph/jest.config.js | 11 + .../index_lifecycle_management/jest.config.js | 11 + .../plugins/index_management/jest.config.js | 11 + x-pack/plugins/infra/jest.config.js | 11 + x-pack/plugins/ingest_manager/jest.config.js | 11 + .../plugins/ingest_pipelines/jest.config.js | 11 + x-pack/plugins/lens/jest.config.js | 11 + .../plugins/license_management/jest.config.js | 11 + x-pack/plugins/licensing/jest.config.js | 11 + x-pack/plugins/lists/jest.config.js | 11 + x-pack/plugins/logstash/jest.config.js | 11 + x-pack/plugins/maps/jest.config.js | 11 + x-pack/plugins/ml/jest.config.js | 11 + x-pack/plugins/monitoring/jest.config.js | 11 + x-pack/plugins/observability/jest.config.js | 35 +- x-pack/plugins/painless_lab/jest.config.js | 11 + x-pack/plugins/remote_clusters/jest.config.js | 11 + x-pack/plugins/reporting/jest.config.js | 11 + x-pack/plugins/rollup/jest.config.js | 11 + x-pack/plugins/runtime_fields/jest.config.js | 11 + .../saved_objects_tagging/jest.config.js | 11 + x-pack/plugins/searchprofiler/jest.config.js | 11 + x-pack/plugins/security/jest.config.js | 11 + .../plugins/security_solution/jest.config.js | 11 + .../plugins/snapshot_restore/jest.config.js | 11 + x-pack/plugins/spaces/jest.config.js | 11 + x-pack/plugins/stack_alerts/jest.config.js | 11 + x-pack/plugins/task_manager/jest.config.js | 11 + .../telemetry_collection_xpack/jest.config.js | 11 + x-pack/plugins/transform/jest.config.js | 11 + .../triggers_actions_ui/jest.config.js | 11 + .../ui_actions_enhanced/jest.config.js | 11 + .../plugins/upgrade_assistant/jest.config.js | 11 + x-pack/plugins/uptime/jest.config.js | 11 + .../jest.config.js | 11 + x-pack/plugins/watcher/jest.config.js | 11 + x-pack/plugins/xpack_legacy/jest.config.js | 11 + x-pack/scripts/jest.js | 14 +- x-pack/scripts/jest_integration.js | 24 - .../monitor_states_real_data.snap | 371 --- .../services/__snapshots__/throughput.snap | 250 --- .../traces/__snapshots__/top_traces.snap | 774 ------- .../__snapshots__/breakdown.snap | 1016 --------- .../__snapshots__/error_rate.snap | 250 --- .../__snapshots__/top_transaction_groups.snap | 126 -- .../__snapshots__/transaction_charts.snap | 1501 ------------- .../csm/__snapshots__/page_load_dist.snap | 824 ------- .../tests/csm/__snapshots__/page_views.snap | 280 --- .../__snapshots__/service_maps.snap | 1995 ----------------- .../transaction_groups_charts.snap | 43 - 177 files changed, 2923 insertions(+), 7690 deletions(-) rename src/dev/jest/config.integration.js => jest.config.integration.js (82%) create mode 100644 jest.config.js create mode 100644 jest.config.oss.js create mode 100644 packages/kbn-apm-config-loader/jest.config.js create mode 100644 packages/kbn-babel-code-parser/jest.config.js create mode 100644 packages/kbn-config-schema/jest.config.js create mode 100644 packages/kbn-config/jest.config.js create mode 100644 packages/kbn-dev-utils/jest.config.js create mode 100644 packages/kbn-es-archiver/jest.config.js create mode 100644 packages/kbn-es/jest.config.js create mode 100644 packages/kbn-i18n/jest.config.js create mode 100644 packages/kbn-interpreter/jest.config.js create mode 100644 packages/kbn-legacy-logging/jest.config.js create mode 100644 packages/kbn-logging/jest.config.js create mode 100644 packages/kbn-monaco/jest.config.js create mode 100644 packages/kbn-optimizer/jest.config.js create mode 100644 packages/kbn-plugin-generator/jest.config.js create mode 100644 packages/kbn-pm/jest.config.js create mode 100644 packages/kbn-release-notes/jest.config.js create mode 100644 packages/kbn-spec-to-console/jest.config.js create mode 100644 packages/kbn-std/jest.config.js create mode 100644 packages/kbn-telemetry-tools/jest.config.js create mode 100644 packages/kbn-test/jest.config.js create mode 100644 packages/kbn-test/src/jest/run_check_jest_configs_cli.ts create mode 100644 packages/kbn-ui-framework/jest.config.js create mode 100644 packages/kbn-utils/jest.config.js rename src/dev/jest/cli.js => scripts/check_jest_configs.js (90%) create mode 100644 src/cli/jest.config.js create mode 100644 src/cli_encryption_keys/jest.config.js create mode 100644 src/cli_keystore/jest.config.js create mode 100644 src/cli_plugin/jest.config.js create mode 100644 src/core/jest.config.js create mode 100644 src/dev/jest.config.js create mode 100644 src/legacy/server/jest.config.js create mode 100644 src/legacy/ui/jest.config.js create mode 100644 src/legacy/utils/jest.config.js create mode 100644 src/optimize/jest.config.js create mode 100644 src/plugins/advanced_settings/jest.config.js create mode 100644 src/plugins/bfetch/jest.config.js create mode 100644 src/plugins/charts/jest.config.js rename src/{dev/jest/config.js => plugins/console/jest.config.js} (59%) create mode 100644 src/plugins/dashboard/jest.config.js create mode 100644 src/plugins/data/jest.config.js create mode 100644 src/plugins/discover/jest.config.js create mode 100644 src/plugins/embeddable/jest.config.js create mode 100644 src/plugins/es_ui_shared/jest.config.js create mode 100644 src/plugins/expressions/jest.config.js create mode 100644 src/plugins/home/jest.config.js create mode 100644 src/plugins/index_pattern_management/jest.config.js create mode 100644 src/plugins/input_control_vis/jest.config.js create mode 100644 src/plugins/inspector/jest.config.js create mode 100644 src/plugins/kibana_legacy/jest.config.js create mode 100644 src/plugins/kibana_overview/jest.config.js create mode 100644 src/plugins/kibana_react/jest.config.js create mode 100644 src/plugins/kibana_usage_collection/jest.config.js create mode 100644 src/plugins/kibana_utils/jest.config.js create mode 100644 src/plugins/legacy_export/jest.config.js create mode 100644 src/plugins/management/jest.config.js create mode 100644 src/plugins/maps_legacy/jest.config.js create mode 100644 src/plugins/navigation/jest.config.js create mode 100644 src/plugins/newsfeed/jest.config.js create mode 100644 src/plugins/region_map/jest.config.js create mode 100644 src/plugins/saved_objects/jest.config.js create mode 100644 src/plugins/saved_objects_management/jest.config.js create mode 100644 src/plugins/saved_objects_tagging_oss/jest.config.js create mode 100644 src/plugins/security_oss/jest.config.js create mode 100644 src/plugins/share/jest.config.js create mode 100644 src/plugins/telemetry/jest.config.js create mode 100644 src/plugins/telemetry_collection_manager/jest.config.js create mode 100644 src/plugins/telemetry_management_section/jest.config.js create mode 100644 src/plugins/tile_map/jest.config.js create mode 100644 src/plugins/ui_actions/jest.config.js create mode 100644 src/plugins/url_forwarding/jest.config.js create mode 100644 src/plugins/usage_collection/jest.config.js create mode 100644 src/plugins/vis_default_editor/jest.config.js create mode 100644 src/plugins/vis_type_markdown/jest.config.js create mode 100644 src/plugins/vis_type_metric/jest.config.js create mode 100644 src/plugins/vis_type_table/jest.config.js create mode 100644 src/plugins/vis_type_tagcloud/jest.config.js create mode 100644 src/plugins/vis_type_timelion/jest.config.js create mode 100644 src/plugins/vis_type_timeseries/jest.config.js create mode 100644 src/plugins/vis_type_vega/jest.config.js create mode 100644 src/plugins/vis_type_vislib/jest.config.js create mode 100644 src/plugins/visualizations/jest.config.js create mode 100644 src/plugins/visualize/jest.config.js create mode 100644 src/setup_node_env/jest.config.js create mode 100644 src/test_utils/jest.config.js create mode 100644 test/functional/jest.config.js create mode 100644 test/scripts/checks/jest_configs.sh delete mode 100644 x-pack/dev-tools/jest/create_jest_config.js delete mode 100644 x-pack/dev-tools/jest/index.js create mode 100644 x-pack/jest.config.js create mode 100644 x-pack/plugins/actions/jest.config.js create mode 100644 x-pack/plugins/alerting_builtins/jest.config.js create mode 100644 x-pack/plugins/alerts/jest.config.js create mode 100644 x-pack/plugins/audit_trail/jest.config.js create mode 100644 x-pack/plugins/beats_management/jest.config.js create mode 100644 x-pack/plugins/canvas/jest.config.js create mode 100644 x-pack/plugins/case/jest.config.js create mode 100644 x-pack/plugins/cloud/jest.config.js create mode 100644 x-pack/plugins/code/jest.config.js create mode 100644 x-pack/plugins/cross_cluster_replication/jest.config.js create mode 100644 x-pack/plugins/dashboard_enhanced/jest.config.js create mode 100644 x-pack/plugins/dashboard_mode/jest.config.js create mode 100644 x-pack/plugins/data_enhanced/jest.config.js create mode 100644 x-pack/plugins/discover_enhanced/jest.config.js create mode 100644 x-pack/plugins/drilldowns/jest.config.js create mode 100644 x-pack/plugins/embeddable_enhanced/jest.config.js create mode 100644 x-pack/plugins/encrypted_saved_objects/jest.config.js create mode 100644 x-pack/plugins/enterprise_search/jest.config.js create mode 100644 x-pack/plugins/event_log/jest.config.js create mode 100644 x-pack/plugins/features/jest.config.js create mode 100644 x-pack/plugins/file_upload/jest.config.js create mode 100644 x-pack/plugins/fleet/jest.config.js create mode 100644 x-pack/plugins/global_search/jest.config.js create mode 100644 x-pack/plugins/global_search_bar/jest.config.js create mode 100644 x-pack/plugins/global_search_providers/jest.config.js create mode 100644 x-pack/plugins/graph/jest.config.js create mode 100644 x-pack/plugins/index_lifecycle_management/jest.config.js create mode 100644 x-pack/plugins/index_management/jest.config.js create mode 100644 x-pack/plugins/infra/jest.config.js create mode 100644 x-pack/plugins/ingest_manager/jest.config.js create mode 100644 x-pack/plugins/ingest_pipelines/jest.config.js create mode 100644 x-pack/plugins/lens/jest.config.js create mode 100644 x-pack/plugins/license_management/jest.config.js create mode 100644 x-pack/plugins/licensing/jest.config.js create mode 100644 x-pack/plugins/lists/jest.config.js create mode 100644 x-pack/plugins/logstash/jest.config.js create mode 100644 x-pack/plugins/maps/jest.config.js create mode 100644 x-pack/plugins/ml/jest.config.js create mode 100644 x-pack/plugins/monitoring/jest.config.js create mode 100644 x-pack/plugins/painless_lab/jest.config.js create mode 100644 x-pack/plugins/remote_clusters/jest.config.js create mode 100644 x-pack/plugins/reporting/jest.config.js create mode 100644 x-pack/plugins/rollup/jest.config.js create mode 100644 x-pack/plugins/runtime_fields/jest.config.js create mode 100644 x-pack/plugins/saved_objects_tagging/jest.config.js create mode 100644 x-pack/plugins/searchprofiler/jest.config.js create mode 100644 x-pack/plugins/security/jest.config.js create mode 100644 x-pack/plugins/security_solution/jest.config.js create mode 100644 x-pack/plugins/snapshot_restore/jest.config.js create mode 100644 x-pack/plugins/spaces/jest.config.js create mode 100644 x-pack/plugins/stack_alerts/jest.config.js create mode 100644 x-pack/plugins/task_manager/jest.config.js create mode 100644 x-pack/plugins/telemetry_collection_xpack/jest.config.js create mode 100644 x-pack/plugins/transform/jest.config.js create mode 100644 x-pack/plugins/triggers_actions_ui/jest.config.js create mode 100644 x-pack/plugins/ui_actions_enhanced/jest.config.js create mode 100644 x-pack/plugins/upgrade_assistant/jest.config.js create mode 100644 x-pack/plugins/uptime/jest.config.js create mode 100644 x-pack/plugins/vis_type_timeseries_enhanced/jest.config.js create mode 100644 x-pack/plugins/watcher/jest.config.js create mode 100644 x-pack/plugins/xpack_legacy/jest.config.js delete mode 100644 x-pack/scripts/jest_integration.js delete mode 100644 x-pack/test/api_integration/apis/uptime/rest/__snapshots__/monitor_states_real_data.snap delete mode 100644 x-pack/test/apm_api_integration/basic/tests/services/__snapshots__/throughput.snap delete mode 100644 x-pack/test/apm_api_integration/basic/tests/traces/__snapshots__/top_traces.snap delete mode 100644 x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/breakdown.snap delete mode 100644 x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/error_rate.snap delete mode 100644 x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/top_transaction_groups.snap delete mode 100644 x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/transaction_charts.snap delete mode 100644 x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_load_dist.snap delete mode 100644 x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_views.snap delete mode 100644 x-pack/test/apm_api_integration/trial/tests/service_maps/__snapshots__/service_maps.snap delete mode 100644 x-pack/test/apm_api_integration/trial/tests/services/__snapshots__/transaction_groups_charts.snap diff --git a/docs/developer/contributing/development-unit-tests.asciidoc b/docs/developer/contributing/development-unit-tests.asciidoc index 5322106b17ac1..d5f5bc76b3302 100644 --- a/docs/developer/contributing/development-unit-tests.asciidoc +++ b/docs/developer/contributing/development-unit-tests.asciidoc @@ -20,11 +20,13 @@ yarn test:mocha == Jest Jest tests are stored in the same directory as source code files with the `.test.{js,mjs,ts,tsx}` suffix. -*Running Jest Unit Tests* +Each plugin and package contains it's own `jest.config.js` file to define its root, and any overrides +to the jest-preset provided by `@kbn/test`. When working on a single plugin or package, you will find +it's more efficient to supply the Jest configuration file when running. ["source","shell"] ----------- -yarn test:jest +yarn jest --config src/plugins/discover/jest.config.js ----------- [discrete] diff --git a/src/dev/jest/config.integration.js b/jest.config.integration.js similarity index 82% rename from src/dev/jest/config.integration.js rename to jest.config.integration.js index 9e7bbc34ac711..3dacb107f94c0 100644 --- a/src/dev/jest/config.integration.js +++ b/jest.config.integration.js @@ -17,16 +17,14 @@ * under the License. */ -import preset from '@kbn/test/jest-preset'; -import config from './config'; +const preset = require('@kbn/test/jest-preset'); -export default { - ...config, - testMatch: [ - '**/integration_tests/**/*.test.js', - '**/integration_tests/**/*.test.ts', - '**/integration_tests/**/*.test.tsx', - ], +module.exports = { + preset: '@kbn/test', + rootDir: '.', + roots: ['/src', '/packages'], + testMatch: ['**/integration_tests**/*.test.{js,mjs,ts,tsx}'], + testRunner: 'jasmine2', testPathIgnorePatterns: preset.testPathIgnorePatterns.filter( (pattern) => !pattern.includes('integration_tests') ), diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000000000..c190556700b81 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,23 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + rootDir: '.', + projects: [...require('./jest.config.oss').projects, ...require('./x-pack/jest.config').projects], +}; diff --git a/jest.config.oss.js b/jest.config.oss.js new file mode 100644 index 0000000000000..e9235069687e0 --- /dev/null +++ b/jest.config.oss.js @@ -0,0 +1,30 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + rootDir: '.', + projects: [ + '/packages/*/jest.config.js', + '/src/*/jest.config.js', + '/src/legacy/*/jest.config.js', + '/src/plugins/*/jest.config.js', + '/test/*/jest.config.js', + ], + reporters: ['default', '/packages/kbn-test/target/jest/junit_reporter'], +}; diff --git a/packages/README.md b/packages/README.md index 8ff05f4e8ff89..9d9cd4ed7b6e5 100644 --- a/packages/README.md +++ b/packages/README.md @@ -60,7 +60,7 @@ A package can also follow the pattern of having `.test.js` files as siblings of A package using the `.test.js` naming convention will have those tests automatically picked up by Jest and run by the unit test runner, currently mapped to the Kibana `test` script in the root `package.json`. * `yarn test` or `yarn grunt test` runs all unit tests. -* `node scripts/jest` runs all Jest tests in Kibana. +* `yarn jest` runs all Jest tests in Kibana. ---- Each package can also specify its own `test` script in the package's `package.json`, for cases where you'd prefer to run the tests from the local package directory. diff --git a/packages/kbn-apm-config-loader/jest.config.js b/packages/kbn-apm-config-loader/jest.config.js new file mode 100644 index 0000000000000..2b88679a57e72 --- /dev/null +++ b/packages/kbn-apm-config-loader/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-apm-config-loader'], +}; diff --git a/packages/kbn-babel-code-parser/jest.config.js b/packages/kbn-babel-code-parser/jest.config.js new file mode 100644 index 0000000000000..60fce8897723e --- /dev/null +++ b/packages/kbn-babel-code-parser/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-babel-code-parser'], +}; diff --git a/packages/kbn-config-schema/jest.config.js b/packages/kbn-config-schema/jest.config.js new file mode 100644 index 0000000000000..35de02838aa1c --- /dev/null +++ b/packages/kbn-config-schema/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-config-schema'], +}; diff --git a/packages/kbn-config/jest.config.js b/packages/kbn-config/jest.config.js new file mode 100644 index 0000000000000..b4c84eef4675c --- /dev/null +++ b/packages/kbn-config/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-config'], +}; diff --git a/packages/kbn-dev-utils/jest.config.js b/packages/kbn-dev-utils/jest.config.js new file mode 100644 index 0000000000000..2b0cefe5e741f --- /dev/null +++ b/packages/kbn-dev-utils/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-dev-utils'], +}; diff --git a/packages/kbn-es-archiver/jest.config.js b/packages/kbn-es-archiver/jest.config.js new file mode 100644 index 0000000000000..e5df757f6637e --- /dev/null +++ b/packages/kbn-es-archiver/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-es-archiver'], +}; diff --git a/packages/kbn-es/jest.config.js b/packages/kbn-es/jest.config.js new file mode 100644 index 0000000000000..2c09b5400369d --- /dev/null +++ b/packages/kbn-es/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-es'], +}; diff --git a/packages/kbn-i18n/jest.config.js b/packages/kbn-i18n/jest.config.js new file mode 100644 index 0000000000000..dff8b872bdfe0 --- /dev/null +++ b/packages/kbn-i18n/jest.config.js @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-i18n'], + testRunner: 'jasmine2', +}; diff --git a/packages/kbn-interpreter/jest.config.js b/packages/kbn-interpreter/jest.config.js new file mode 100644 index 0000000000000..d2f6127ccff79 --- /dev/null +++ b/packages/kbn-interpreter/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-interpreter'], +}; diff --git a/packages/kbn-legacy-logging/jest.config.js b/packages/kbn-legacy-logging/jest.config.js new file mode 100644 index 0000000000000..f33205439e134 --- /dev/null +++ b/packages/kbn-legacy-logging/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-legacy-logging'], +}; diff --git a/packages/kbn-logging/jest.config.js b/packages/kbn-logging/jest.config.js new file mode 100644 index 0000000000000..74ff8fd14f56a --- /dev/null +++ b/packages/kbn-logging/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-logging'], +}; diff --git a/packages/kbn-monaco/jest.config.js b/packages/kbn-monaco/jest.config.js new file mode 100644 index 0000000000000..03f879f6d0bef --- /dev/null +++ b/packages/kbn-monaco/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-monaco'], +}; diff --git a/packages/kbn-optimizer/jest.config.js b/packages/kbn-optimizer/jest.config.js new file mode 100644 index 0000000000000..6e313aaad3c82 --- /dev/null +++ b/packages/kbn-optimizer/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-optimizer'], +}; diff --git a/packages/kbn-plugin-generator/jest.config.js b/packages/kbn-plugin-generator/jest.config.js new file mode 100644 index 0000000000000..1d81a72128afd --- /dev/null +++ b/packages/kbn-plugin-generator/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-plugin-generator'], +}; diff --git a/packages/kbn-pm/jest.config.js b/packages/kbn-pm/jest.config.js new file mode 100644 index 0000000000000..ba0624f5f6ccd --- /dev/null +++ b/packages/kbn-pm/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-pm'], +}; diff --git a/packages/kbn-release-notes/jest.config.js b/packages/kbn-release-notes/jest.config.js new file mode 100644 index 0000000000000..44390a8c98162 --- /dev/null +++ b/packages/kbn-release-notes/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-release-notes'], +}; diff --git a/packages/kbn-spec-to-console/jest.config.js b/packages/kbn-spec-to-console/jest.config.js new file mode 100644 index 0000000000000..cef82f4d76f73 --- /dev/null +++ b/packages/kbn-spec-to-console/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-spec-to-console'], +}; diff --git a/packages/kbn-std/jest.config.js b/packages/kbn-std/jest.config.js new file mode 100644 index 0000000000000..0615e33e41af8 --- /dev/null +++ b/packages/kbn-std/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-std'], +}; diff --git a/packages/kbn-telemetry-tools/jest.config.js b/packages/kbn-telemetry-tools/jest.config.js new file mode 100644 index 0000000000000..b7b101beccf32 --- /dev/null +++ b/packages/kbn-telemetry-tools/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-telemetry-tools'], +}; diff --git a/packages/kbn-test/jest.config.js b/packages/kbn-test/jest.config.js new file mode 100644 index 0000000000000..9400d402a1a33 --- /dev/null +++ b/packages/kbn-test/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-test'], +}; diff --git a/packages/kbn-test/src/index.ts b/packages/kbn-test/src/index.ts index 9e6ba67a421ac..54b064f5cd49e 100644 --- a/packages/kbn-test/src/index.ts +++ b/packages/kbn-test/src/index.ts @@ -60,3 +60,5 @@ export { CI_PARALLEL_PROCESS_PREFIX } from './ci_parallel_process_prefix'; export * from './functional_test_runner'; export { getUrl } from './jest/utils/get_url'; + +export { runCheckJestConfigsCli } from './jest/run_check_jest_configs_cli'; diff --git a/packages/kbn-test/src/jest/run_check_jest_configs_cli.ts b/packages/kbn-test/src/jest/run_check_jest_configs_cli.ts new file mode 100644 index 0000000000000..385fb453697ef --- /dev/null +++ b/packages/kbn-test/src/jest/run_check_jest_configs_cli.ts @@ -0,0 +1,113 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { relative, resolve, sep } from 'path'; +import { writeFileSync } from 'fs'; + +import execa from 'execa'; +import globby from 'globby'; +import Mustache from 'mustache'; + +import { run } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; + +// @ts-ignore +import { testMatch } from '../../jest-preset'; + +const template: string = `module.exports = { + preset: '@kbn/test', + rootDir: '{{{relToRoot}}}', + roots: ['/{{{modulePath}}}'], +}; +`; + +const roots: string[] = ['x-pack/plugins', 'packages', 'src/legacy', 'src/plugins', 'test', 'src']; + +export async function runCheckJestConfigsCli() { + run( + async ({ flags: { fix = false }, log }) => { + const { stdout: coveredFiles } = await execa( + 'yarn', + ['--silent', 'jest', '--listTests', '--json'], + { + cwd: REPO_ROOT, + } + ); + + const allFiles = new Set( + await globby(testMatch.concat(['!**/integration_tests/**']), { + gitignore: true, + }) + ); + + JSON.parse(coveredFiles).forEach((file: string) => { + const pathFromRoot = relative(REPO_ROOT, file); + allFiles.delete(pathFromRoot); + }); + + if (allFiles.size) { + log.error( + `The following files do not belong to a jest.config.js file, or that config is not included from the root jest.config.js\n${[ + ...allFiles, + ] + .map((file) => ` - ${file}`) + .join('\n')}` + ); + } else { + log.success('All test files are included by a Jest configuration'); + return; + } + + if (fix) { + allFiles.forEach((file) => { + const root = roots.find((r) => file.startsWith(r)); + + if (root) { + const name = relative(root, file).split(sep)[0]; + const modulePath = [root, name].join('/'); + + const content = Mustache.render(template, { + relToRoot: relative(modulePath, '.'), + modulePath, + }); + + writeFileSync(resolve(root, name, 'jest.config.js'), content); + } else { + log.warning(`Unable to determind where to place jest.config.js for ${file}`); + } + }); + } else { + log.info( + `Run 'node scripts/check_jest_configs --fix' to attempt to create the missing config files` + ); + } + + process.exit(1); + }, + { + description: 'Check that all test files are covered by a jest.config.js', + flags: { + boolean: ['fix'], + help: ` + --fix Attempt to create missing config files + `, + }, + } + ); +} diff --git a/packages/kbn-ui-framework/jest.config.js b/packages/kbn-ui-framework/jest.config.js new file mode 100644 index 0000000000000..d9cb93d7c069d --- /dev/null +++ b/packages/kbn-ui-framework/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-ui-framework'], +}; diff --git a/packages/kbn-utils/jest.config.js b/packages/kbn-utils/jest.config.js new file mode 100644 index 0000000000000..39fb0a8ff1a8c --- /dev/null +++ b/packages/kbn-utils/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-utils'], +}; diff --git a/src/dev/jest/cli.js b/scripts/check_jest_configs.js similarity index 90% rename from src/dev/jest/cli.js rename to scripts/check_jest_configs.js index 40627c4bece74..a7a520f433bf9 100644 --- a/src/dev/jest/cli.js +++ b/scripts/check_jest_configs.js @@ -17,6 +17,5 @@ * under the License. */ -import { run } from 'jest'; - -run(process.argv.slice(2)); +require('../src/setup_node_env'); +require('@kbn/test').runCheckJestConfigsCli(); diff --git a/scripts/jest.js b/scripts/jest.js index c252056de766b..90f8da10f4c90 100755 --- a/scripts/jest.js +++ b/scripts/jest.js @@ -29,8 +29,15 @@ // // See all cli options in https://facebook.github.io/jest/docs/cli.html -var resolve = require('path').resolve; -process.argv.push('--config', resolve(__dirname, '../src/dev/jest/config.js')); +if (process.argv.indexOf('--config') === -1) { + // append correct jest.config if none is provided + var configPath = require('path').resolve(__dirname, '../jest.config.oss.js'); + process.argv.push('--config', configPath); + console.log('Running Jest with --config', configPath); +} -require('../src/setup_node_env'); -require('../src/dev/jest/cli'); +if (process.env.NODE_ENV == null) { + process.env.NODE_ENV = 'test'; +} + +require('jest').run(); diff --git a/scripts/jest_integration.js b/scripts/jest_integration.js index 7da1436f5583c..f07d28939ef0c 100755 --- a/scripts/jest_integration.js +++ b/scripts/jest_integration.js @@ -29,9 +29,17 @@ // // See all cli options in https://facebook.github.io/jest/docs/cli.html -var resolve = require('path').resolve; -process.argv.push('--config', resolve(__dirname, '../src/dev/jest/config.integration.js')); process.argv.push('--runInBand'); -require('../src/setup_node_env'); -require('../src/dev/jest/cli'); +if (process.argv.indexOf('--config') === -1) { + // append correct jest.config if none is provided + var configPath = require('path').resolve(__dirname, '../jest.config.integration.js'); + process.argv.push('--config', configPath); + console.log('Running Jest with --config', configPath); +} + +if (process.env.NODE_ENV == null) { + process.env.NODE_ENV = 'test'; +} + +require('jest').run(); diff --git a/src/cli/jest.config.js b/src/cli/jest.config.js new file mode 100644 index 0000000000000..6a1055ca864c8 --- /dev/null +++ b/src/cli/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/src/cli'], +}; diff --git a/src/cli_encryption_keys/jest.config.js b/src/cli_encryption_keys/jest.config.js new file mode 100644 index 0000000000000..f3be28f7898f5 --- /dev/null +++ b/src/cli_encryption_keys/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/src/cli_encryption_keys'], +}; diff --git a/src/cli_keystore/jest.config.js b/src/cli_keystore/jest.config.js new file mode 100644 index 0000000000000..787cd7ccd84be --- /dev/null +++ b/src/cli_keystore/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/src/cli_keystore'], +}; diff --git a/src/cli_plugin/jest.config.js b/src/cli_plugin/jest.config.js new file mode 100644 index 0000000000000..cbd226f5df887 --- /dev/null +++ b/src/cli_plugin/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/src/cli_plugin'], +}; diff --git a/src/core/jest.config.js b/src/core/jest.config.js new file mode 100644 index 0000000000000..bdb65b3817507 --- /dev/null +++ b/src/core/jest.config.js @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/src/core'], + testRunner: 'jasmine2', +}; diff --git a/src/dev/code_coverage/ingest_coverage/integration_tests/team_assignment.test.js b/src/dev/code_coverage/ingest_coverage/integration_tests/team_assignment.test.js index c666581ddb08c..177439c56a115 100644 --- a/src/dev/code_coverage/ingest_coverage/integration_tests/team_assignment.test.js +++ b/src/dev/code_coverage/ingest_coverage/integration_tests/team_assignment.test.js @@ -49,7 +49,8 @@ describe('Team Assignment', () => { describe(`when the codeowners file contains #CC#`, () => { it(`should strip the prefix and still drill down through the fs`, async () => { const { stdout } = await execa('grep', ['tre', teamAssignmentsPath], { cwd: ROOT_DIR }); - expect(stdout).to.be(`x-pack/plugins/code/server/config.ts kibana-tre + expect(stdout).to.be(`x-pack/plugins/code/jest.config.js kibana-tre +x-pack/plugins/code/server/config.ts kibana-tre x-pack/plugins/code/server/index.ts kibana-tre x-pack/plugins/code/server/plugin.test.ts kibana-tre x-pack/plugins/code/server/plugin.ts kibana-tre`); diff --git a/src/dev/jest.config.js b/src/dev/jest.config.js new file mode 100644 index 0000000000000..bdb51372e2c26 --- /dev/null +++ b/src/dev/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/src/dev'], +}; diff --git a/src/legacy/server/jest.config.js b/src/legacy/server/jest.config.js new file mode 100644 index 0000000000000..f971e823765ac --- /dev/null +++ b/src/legacy/server/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/legacy/server'], +}; diff --git a/src/legacy/ui/jest.config.js b/src/legacy/ui/jest.config.js new file mode 100644 index 0000000000000..45809f8797129 --- /dev/null +++ b/src/legacy/ui/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/legacy/ui'], +}; diff --git a/src/legacy/utils/jest.config.js b/src/legacy/utils/jest.config.js new file mode 100644 index 0000000000000..7ce73fa367613 --- /dev/null +++ b/src/legacy/utils/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/legacy/utils'], +}; diff --git a/src/optimize/jest.config.js b/src/optimize/jest.config.js new file mode 100644 index 0000000000000..419f4f97098b3 --- /dev/null +++ b/src/optimize/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/src/optimize'], +}; diff --git a/src/plugins/advanced_settings/jest.config.js b/src/plugins/advanced_settings/jest.config.js new file mode 100644 index 0000000000000..94fd65aae4464 --- /dev/null +++ b/src/plugins/advanced_settings/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/advanced_settings'], +}; diff --git a/src/plugins/bfetch/jest.config.js b/src/plugins/bfetch/jest.config.js new file mode 100644 index 0000000000000..5976a994be7e5 --- /dev/null +++ b/src/plugins/bfetch/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/bfetch'], +}; diff --git a/src/plugins/charts/jest.config.js b/src/plugins/charts/jest.config.js new file mode 100644 index 0000000000000..168ccde71a667 --- /dev/null +++ b/src/plugins/charts/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/charts'], +}; diff --git a/src/dev/jest/config.js b/src/plugins/console/jest.config.js similarity index 59% rename from src/dev/jest/config.js rename to src/plugins/console/jest.config.js index c04ef9480d0b7..f08613f91e1f1 100644 --- a/src/dev/jest/config.js +++ b/src/plugins/console/jest.config.js @@ -17,26 +17,9 @@ * under the License. */ -export default { +module.exports = { preset: '@kbn/test', rootDir: '../../..', - roots: [ - '/src/plugins', - '/src/legacy/ui', - '/src/core', - '/src/legacy/server', - '/src/cli', - '/src/cli_keystore', - '/src/cli_encryption_keys', - '/src/cli_plugin', - '/packages/kbn-test/target/functional_test_runner', - '/src/dev', - '/src/optimize', - '/src/legacy/utils', - '/src/setup_node_env', - '/packages', - '/test/functional/services/remote', - '/src/dev/code_coverage/ingest_coverage', - ], + roots: ['/src/plugins/console'], testRunner: 'jasmine2', }; diff --git a/src/plugins/dashboard/jest.config.js b/src/plugins/dashboard/jest.config.js new file mode 100644 index 0000000000000..b9f6f66159b30 --- /dev/null +++ b/src/plugins/dashboard/jest.config.js @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/dashboard'], + testRunner: 'jasmine2', +}; diff --git a/src/plugins/data/jest.config.js b/src/plugins/data/jest.config.js new file mode 100644 index 0000000000000..3c6e854a53d7b --- /dev/null +++ b/src/plugins/data/jest.config.js @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/data'], + testRunner: 'jasmine2', +}; diff --git a/src/plugins/discover/jest.config.js b/src/plugins/discover/jest.config.js new file mode 100644 index 0000000000000..0723569db357d --- /dev/null +++ b/src/plugins/discover/jest.config.js @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/discover'], + testRunner: 'jasmine2', +}; diff --git a/src/plugins/embeddable/jest.config.js b/src/plugins/embeddable/jest.config.js new file mode 100644 index 0000000000000..a079791092549 --- /dev/null +++ b/src/plugins/embeddable/jest.config.js @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/embeddable'], + testRunner: 'jasmine2', +}; diff --git a/src/plugins/es_ui_shared/jest.config.js b/src/plugins/es_ui_shared/jest.config.js new file mode 100644 index 0000000000000..5b8b34692800d --- /dev/null +++ b/src/plugins/es_ui_shared/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/es_ui_shared'], +}; diff --git a/src/plugins/expressions/jest.config.js b/src/plugins/expressions/jest.config.js new file mode 100644 index 0000000000000..b4e3e10b3fc70 --- /dev/null +++ b/src/plugins/expressions/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/expressions'], +}; diff --git a/src/plugins/home/jest.config.js b/src/plugins/home/jest.config.js new file mode 100644 index 0000000000000..c56c7b3eed1d6 --- /dev/null +++ b/src/plugins/home/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/home'], +}; diff --git a/src/plugins/index_pattern_management/jest.config.js b/src/plugins/index_pattern_management/jest.config.js new file mode 100644 index 0000000000000..8a499406080fd --- /dev/null +++ b/src/plugins/index_pattern_management/jest.config.js @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/index_pattern_management'], + testRunner: 'jasmine2', +}; diff --git a/src/plugins/input_control_vis/jest.config.js b/src/plugins/input_control_vis/jest.config.js new file mode 100644 index 0000000000000..17fb6f3359bf3 --- /dev/null +++ b/src/plugins/input_control_vis/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/input_control_vis'], +}; diff --git a/src/plugins/inspector/jest.config.js b/src/plugins/inspector/jest.config.js new file mode 100644 index 0000000000000..6fc4a063970b9 --- /dev/null +++ b/src/plugins/inspector/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/inspector'], +}; diff --git a/src/plugins/kibana_legacy/jest.config.js b/src/plugins/kibana_legacy/jest.config.js new file mode 100644 index 0000000000000..69df43bc5b15f --- /dev/null +++ b/src/plugins/kibana_legacy/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/kibana_legacy'], +}; diff --git a/src/plugins/kibana_overview/jest.config.js b/src/plugins/kibana_overview/jest.config.js new file mode 100644 index 0000000000000..4a719b38e47ae --- /dev/null +++ b/src/plugins/kibana_overview/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/kibana_overview'], +}; diff --git a/src/plugins/kibana_react/jest.config.js b/src/plugins/kibana_react/jest.config.js new file mode 100644 index 0000000000000..2810331c9b667 --- /dev/null +++ b/src/plugins/kibana_react/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/kibana_react'], +}; diff --git a/src/plugins/kibana_usage_collection/jest.config.js b/src/plugins/kibana_usage_collection/jest.config.js new file mode 100644 index 0000000000000..9510fc98732b3 --- /dev/null +++ b/src/plugins/kibana_usage_collection/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/kibana_usage_collection'], +}; diff --git a/src/plugins/kibana_utils/jest.config.js b/src/plugins/kibana_utils/jest.config.js new file mode 100644 index 0000000000000..2ddfb7047bf2e --- /dev/null +++ b/src/plugins/kibana_utils/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/kibana_utils'], +}; diff --git a/src/plugins/legacy_export/jest.config.js b/src/plugins/legacy_export/jest.config.js new file mode 100644 index 0000000000000..1480049fd8f85 --- /dev/null +++ b/src/plugins/legacy_export/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/legacy_export'], +}; diff --git a/src/plugins/management/jest.config.js b/src/plugins/management/jest.config.js new file mode 100644 index 0000000000000..287bafc4b1c11 --- /dev/null +++ b/src/plugins/management/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/management'], +}; diff --git a/src/plugins/maps_legacy/jest.config.js b/src/plugins/maps_legacy/jest.config.js new file mode 100644 index 0000000000000..849bd2957ba62 --- /dev/null +++ b/src/plugins/maps_legacy/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/maps_legacy'], +}; diff --git a/src/plugins/navigation/jest.config.js b/src/plugins/navigation/jest.config.js new file mode 100644 index 0000000000000..bc999a25854de --- /dev/null +++ b/src/plugins/navigation/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/navigation'], +}; diff --git a/src/plugins/newsfeed/jest.config.js b/src/plugins/newsfeed/jest.config.js new file mode 100644 index 0000000000000..bf530497bcbad --- /dev/null +++ b/src/plugins/newsfeed/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/newsfeed'], +}; diff --git a/src/plugins/region_map/jest.config.js b/src/plugins/region_map/jest.config.js new file mode 100644 index 0000000000000..c0d4e4d40bb3a --- /dev/null +++ b/src/plugins/region_map/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/region_map'], +}; diff --git a/src/plugins/saved_objects/jest.config.js b/src/plugins/saved_objects/jest.config.js new file mode 100644 index 0000000000000..00ab010bc61ba --- /dev/null +++ b/src/plugins/saved_objects/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/saved_objects'], +}; diff --git a/src/plugins/saved_objects_management/jest.config.js b/src/plugins/saved_objects_management/jest.config.js new file mode 100644 index 0000000000000..3cedb8c937f5e --- /dev/null +++ b/src/plugins/saved_objects_management/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/saved_objects_management'], +}; diff --git a/src/plugins/saved_objects_tagging_oss/jest.config.js b/src/plugins/saved_objects_tagging_oss/jest.config.js new file mode 100644 index 0000000000000..7e75b5c5593e7 --- /dev/null +++ b/src/plugins/saved_objects_tagging_oss/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/saved_objects_tagging_oss'], +}; diff --git a/src/plugins/security_oss/jest.config.js b/src/plugins/security_oss/jest.config.js new file mode 100644 index 0000000000000..3bf6ee33d3e48 --- /dev/null +++ b/src/plugins/security_oss/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/security_oss'], +}; diff --git a/src/plugins/share/jest.config.js b/src/plugins/share/jest.config.js new file mode 100644 index 0000000000000..39b048279e73b --- /dev/null +++ b/src/plugins/share/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/share'], +}; diff --git a/src/plugins/telemetry/jest.config.js b/src/plugins/telemetry/jest.config.js new file mode 100644 index 0000000000000..914cea68cd01b --- /dev/null +++ b/src/plugins/telemetry/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/telemetry'], +}; diff --git a/src/plugins/telemetry_collection_manager/jest.config.js b/src/plugins/telemetry_collection_manager/jest.config.js new file mode 100644 index 0000000000000..9278ca21d7bc2 --- /dev/null +++ b/src/plugins/telemetry_collection_manager/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/telemetry_collection_manager'], +}; diff --git a/src/plugins/telemetry_management_section/jest.config.js b/src/plugins/telemetry_management_section/jest.config.js new file mode 100644 index 0000000000000..a38fa84b08afc --- /dev/null +++ b/src/plugins/telemetry_management_section/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/telemetry_management_section'], +}; diff --git a/src/plugins/tile_map/jest.config.js b/src/plugins/tile_map/jest.config.js new file mode 100644 index 0000000000000..9a89247b4f782 --- /dev/null +++ b/src/plugins/tile_map/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/tile_map'], +}; diff --git a/src/plugins/ui_actions/jest.config.js b/src/plugins/ui_actions/jest.config.js new file mode 100644 index 0000000000000..3a7de575ea248 --- /dev/null +++ b/src/plugins/ui_actions/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/ui_actions'], +}; diff --git a/src/plugins/url_forwarding/jest.config.js b/src/plugins/url_forwarding/jest.config.js new file mode 100644 index 0000000000000..9dcbfccfcf90a --- /dev/null +++ b/src/plugins/url_forwarding/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/url_forwarding'], +}; diff --git a/src/plugins/usage_collection/jest.config.js b/src/plugins/usage_collection/jest.config.js new file mode 100644 index 0000000000000..89b7fc70fd620 --- /dev/null +++ b/src/plugins/usage_collection/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/usage_collection'], +}; diff --git a/src/plugins/vis_default_editor/jest.config.js b/src/plugins/vis_default_editor/jest.config.js new file mode 100644 index 0000000000000..618f9734fb54c --- /dev/null +++ b/src/plugins/vis_default_editor/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/vis_default_editor'], +}; diff --git a/src/plugins/vis_type_markdown/jest.config.js b/src/plugins/vis_type_markdown/jest.config.js new file mode 100644 index 0000000000000..bff1b12641c92 --- /dev/null +++ b/src/plugins/vis_type_markdown/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/vis_type_markdown'], +}; diff --git a/src/plugins/vis_type_metric/jest.config.js b/src/plugins/vis_type_metric/jest.config.js new file mode 100644 index 0000000000000..5c50fc5f4368e --- /dev/null +++ b/src/plugins/vis_type_metric/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/vis_type_metric'], +}; diff --git a/src/plugins/vis_type_table/jest.config.js b/src/plugins/vis_type_table/jest.config.js new file mode 100644 index 0000000000000..3aa02089df012 --- /dev/null +++ b/src/plugins/vis_type_table/jest.config.js @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/vis_type_table'], + testRunner: 'jasmine2', +}; diff --git a/src/plugins/vis_type_tagcloud/jest.config.js b/src/plugins/vis_type_tagcloud/jest.config.js new file mode 100644 index 0000000000000..5419ca05cca84 --- /dev/null +++ b/src/plugins/vis_type_tagcloud/jest.config.js @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/vis_type_tagcloud'], + testRunner: 'jasmine2', +}; diff --git a/src/plugins/vis_type_timelion/jest.config.js b/src/plugins/vis_type_timelion/jest.config.js new file mode 100644 index 0000000000000..eae12936427f4 --- /dev/null +++ b/src/plugins/vis_type_timelion/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/vis_type_timelion'], +}; diff --git a/src/plugins/vis_type_timeseries/jest.config.js b/src/plugins/vis_type_timeseries/jest.config.js new file mode 100644 index 0000000000000..16c001e598188 --- /dev/null +++ b/src/plugins/vis_type_timeseries/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/vis_type_timeseries'], +}; diff --git a/src/plugins/vis_type_vega/jest.config.js b/src/plugins/vis_type_vega/jest.config.js new file mode 100644 index 0000000000000..a9ae68df0d89b --- /dev/null +++ b/src/plugins/vis_type_vega/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/vis_type_vega'], +}; diff --git a/src/plugins/vis_type_vislib/jest.config.js b/src/plugins/vis_type_vislib/jest.config.js new file mode 100644 index 0000000000000..1324ec1404b3e --- /dev/null +++ b/src/plugins/vis_type_vislib/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/vis_type_vislib'], +}; diff --git a/src/plugins/visualizations/jest.config.js b/src/plugins/visualizations/jest.config.js new file mode 100644 index 0000000000000..b1c5067cfe4a9 --- /dev/null +++ b/src/plugins/visualizations/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/visualizations'], +}; diff --git a/src/plugins/visualize/jest.config.js b/src/plugins/visualize/jest.config.js new file mode 100644 index 0000000000000..6657f4092068f --- /dev/null +++ b/src/plugins/visualize/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/visualize'], +}; diff --git a/src/setup_node_env/jest.config.js b/src/setup_node_env/jest.config.js new file mode 100644 index 0000000000000..61e3239905836 --- /dev/null +++ b/src/setup_node_env/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/src/setup_node_env'], +}; diff --git a/src/test_utils/jest.config.js b/src/test_utils/jest.config.js new file mode 100644 index 0000000000000..b7e77413598c0 --- /dev/null +++ b/src/test_utils/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/src/test_utils'], +}; diff --git a/test/functional/jest.config.js b/test/functional/jest.config.js new file mode 100644 index 0000000000000..60dce5d95773a --- /dev/null +++ b/test/functional/jest.config.js @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/test/functional'], +}; diff --git a/test/scripts/checks/jest_configs.sh b/test/scripts/checks/jest_configs.sh new file mode 100644 index 0000000000000..28cb1386c748f --- /dev/null +++ b/test/scripts/checks/jest_configs.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +source src/dev/ci_setup/setup_env.sh + +checks-reporter-with-killswitch "Check Jest Configs" node scripts/check_jest_configs diff --git a/vars/tasks.groovy b/vars/tasks.groovy index 89302e91ad479..fd96c2bbf8e78 100644 --- a/vars/tasks.groovy +++ b/vars/tasks.groovy @@ -6,6 +6,7 @@ def check() { tasks([ kibanaPipeline.scriptTask('Check Telemetry Schema', 'test/scripts/checks/telemetry.sh'), kibanaPipeline.scriptTask('Check TypeScript Projects', 'test/scripts/checks/ts_projects.sh'), + kibanaPipeline.scriptTask('Check Jest Configs', 'test/scripts/checks/jest_configs.sh'), kibanaPipeline.scriptTask('Check Doc API Changes', 'test/scripts/checks/doc_api_changes.sh'), kibanaPipeline.scriptTask('Check Types', 'test/scripts/checks/type_check.sh'), kibanaPipeline.scriptTask('Check Bundle Limits', 'test/scripts/checks/bundle_limits.sh'), diff --git a/x-pack/dev-tools/jest/create_jest_config.js b/x-pack/dev-tools/jest/create_jest_config.js deleted file mode 100644 index 2ddc58500d15e..0000000000000 --- a/x-pack/dev-tools/jest/create_jest_config.js +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export function createJestConfig({ kibanaDirectory, rootDir }) { - return { - preset: '@kbn/test', - rootDir: kibanaDirectory, - roots: [`${rootDir}/plugins`], - reporters: [ - 'default', - [ - `${kibanaDirectory}/packages/kbn-test/target/jest/junit_reporter`, - { - reportName: 'X-Pack Jest Tests', - }, - ], - ], - }; -} diff --git a/x-pack/dev-tools/jest/index.js b/x-pack/dev-tools/jest/index.js deleted file mode 100644 index c22f8625c5778..0000000000000 --- a/x-pack/dev-tools/jest/index.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { run } from 'jest'; -import { resolve } from 'path'; - -import { createJestConfig } from './create_jest_config'; - -export function runJest() { - process.env.NODE_ENV = process.env.NODE_ENV || 'test'; - const config = JSON.stringify( - createJestConfig({ - kibanaDirectory: resolve(__dirname, '../../..'), - rootDir: resolve(__dirname, '../..'), - xPackKibanaDirectory: resolve(__dirname, '../..'), - }) - ); - - const argv = [...process.argv.slice(2), '--config', config]; - - return run(argv); -} diff --git a/x-pack/jest.config.js b/x-pack/jest.config.js new file mode 100644 index 0000000000000..8b3f717b40e66 --- /dev/null +++ b/x-pack/jest.config.js @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + rootDir: '..', + projects: ['/x-pack/plugins/*/jest.config.js'], + reporters: [ + 'default', + ['/packages/kbn-test/target/jest/junit_reporter', { reportName: 'X-Pack Jest Tests' }], + ], +}; diff --git a/x-pack/plugins/actions/jest.config.js b/x-pack/plugins/actions/jest.config.js new file mode 100644 index 0000000000000..2aaa277079ad3 --- /dev/null +++ b/x-pack/plugins/actions/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/actions'], +}; diff --git a/x-pack/plugins/alerting_builtins/jest.config.js b/x-pack/plugins/alerting_builtins/jest.config.js new file mode 100644 index 0000000000000..05fe793a157df --- /dev/null +++ b/x-pack/plugins/alerting_builtins/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/alerting_builtins'], +}; diff --git a/x-pack/plugins/alerts/jest.config.js b/x-pack/plugins/alerts/jest.config.js new file mode 100644 index 0000000000000..d5c9ce5bbf4c2 --- /dev/null +++ b/x-pack/plugins/alerts/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/alerts'], +}; diff --git a/x-pack/plugins/apm/jest.config.js b/x-pack/plugins/apm/jest.config.js index 2a5ef9ad0c2a7..a0e98eebf65cb 100644 --- a/x-pack/plugins/apm/jest.config.js +++ b/x-pack/plugins/apm/jest.config.js @@ -4,34 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -// This is an APM-specific Jest configuration which overrides the x-pack -// configuration. It's intended for use in development and does not run in CI, -// which runs the entire x-pack suite. Run `npx jest`. - -require('../../../src/setup_node_env'); - -const { createJestConfig } = require('../../dev-tools/jest/create_jest_config'); -const { resolve } = require('path'); - -const rootDir = resolve(__dirname, '.'); -const kibanaDirectory = resolve(__dirname, '../../..'); - -const jestConfig = createJestConfig({ kibanaDirectory, rootDir }); - module.exports = { - ...jestConfig, - reporters: ['default'], - roots: [`${rootDir}/common`, `${rootDir}/public`, `${rootDir}/server`], - collectCoverage: true, - collectCoverageFrom: [ - ...(jestConfig.collectCoverageFrom || []), - '**/*.{js,mjs,jsx,ts,tsx}', - '!**/*.stories.{js,mjs,ts,tsx}', - '!**/dev_docs/**', - '!**/e2e/**', - '!**/target/**', - '!**/typings/**', - ], - coverageDirectory: `${rootDir}/target/coverage/jest`, - coverageReporters: ['html'], + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/apm'], }; diff --git a/x-pack/plugins/audit_trail/jest.config.js b/x-pack/plugins/audit_trail/jest.config.js new file mode 100644 index 0000000000000..31de78fc6bbd9 --- /dev/null +++ b/x-pack/plugins/audit_trail/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/audit_trail'], +}; diff --git a/x-pack/plugins/beats_management/jest.config.js b/x-pack/plugins/beats_management/jest.config.js new file mode 100644 index 0000000000000..8ffbb97b1656d --- /dev/null +++ b/x-pack/plugins/beats_management/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/beats_management'], +}; diff --git a/x-pack/plugins/canvas/jest.config.js b/x-pack/plugins/canvas/jest.config.js new file mode 100644 index 0000000000000..d010fb8c150bc --- /dev/null +++ b/x-pack/plugins/canvas/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/canvas'], +}; diff --git a/x-pack/plugins/canvas/scripts/jest.js b/x-pack/plugins/canvas/scripts/jest.js index a91431a0141c5..9dd8bac88e1e2 100644 --- a/x-pack/plugins/canvas/scripts/jest.js +++ b/x-pack/plugins/canvas/scripts/jest.js @@ -4,89 +4,37 @@ * you may not use this file except in compliance with the Elastic License. */ -const { run } = require('@kbn/dev-utils'); -const { runXPackScript } = require('./_helpers'); +const { resolve } = require('path'); +process.argv.push('--config', resolve(__dirname, '../jest.config.js')); -// Due to https://github.com/facebook/jest/issues/7267, folders that start with `.` -// are ignored if using watchman. Disabling watchman makes testing slow. So -// we're making this script allow -run( - ({ log, flags }) => { - const { all, storybook, update, coverage } = flags; - let { path } = flags; - let options = []; - process.argv.splice(2, process.argv.length - 2); +const storybookPosition = process.argv.indexOf('--storybook'); +const allPosition = process.argv.indexOf('--all'); - if (path) { - log.info(`Limiting tests to ${path}...`); - path = 'plugins/canvas/' + path; - } else { - path = 'plugins/canvas'; - } +console.log(` +A helper proxying to the following command: - if (coverage) { - log.info(`Collecting test coverage and writing to canvas/coverage...`); - options = [ - '--coverage', - '--collectCoverageFrom', // Ignore TS definition files - `!${path}/**/*.d.ts`, - '--collectCoverageFrom', // Ignore build directories - `!${path}/**/build/**`, - '--collectCoverageFrom', // Ignore coverage on test files - `!${path}/**/__tests__/**/*`, - '--collectCoverageFrom', // Ignore coverage on example files - `!${path}/**/__examples__/**/*`, - '--collectCoverageFrom', // Ignore flot files - `!${path}/**/flot-charts/**`, - '--collectCoverageFrom', // Ignore coverage files - `!${path}/**/coverage/**`, - '--collectCoverageFrom', // Ignore scripts - `!${path}/**/scripts/**`, - '--collectCoverageFrom', // Ignore mock files - `!${path}/**/mocks/**`, - '--collectCoverageFrom', // Include JS files - `${path}/**/*.js`, - '--collectCoverageFrom', // Include TS/X files - `${path}/**/*.ts*`, - '--coverageDirectory', // Output to canvas/coverage - 'plugins/canvas/coverage', - ]; - } - // Mitigation for https://github.com/facebook/jest/issues/7267 - if (all || storybook) { - options = options.concat(['--no-cache', '--no-watchman']); - } + yarn jest --config x-pack/plugins/canvas/jest.config.js - if (all) { - log.info('Running all available tests. This will take a while...'); - } else if (storybook) { - path = 'plugins/canvas/storybook'; - log.info('Running Storybook Snapshot tests...'); - } else { - log.info('Running tests. This does not include Storybook Snapshots...'); - } +Provides the following additional options: + --all Runs all tests and snapshots. Slower. + --storybook Runs Storybook Snapshot tests only. +`); - if (update) { - log.info('Updating any Jest snapshots...'); - options.push('-u'); - } +if (storybookPosition > -1) { + process.argv.splice(storybookPosition, 1); - runXPackScript('jest', [path].concat(options)); - }, - { - description: ` - Jest test runner for Canvas. By default, will not include Storybook Snapshots. - `, - flags: { - boolean: ['all', 'storybook', 'update', 'coverage'], - string: ['path'], - help: ` - --all Runs all tests and snapshots. Slower. - --storybook Runs Storybook Snapshot tests only. - --update Updates Storybook Snapshot tests. - --path Runs any tests at a given path. - --coverage Collect coverage statistics. - `, - }, - } -); + console.log('Running Storybook Snapshot tests only'); + process.argv.push('canvas/storybook/'); +} else if (allPosition > -1) { + process.argv.splice(allPosition, 1); + console.log('Running all available tests. This will take a while...'); +} else { + console.log('Running tests. This does not include Storybook Snapshots...'); + process.argv.push('--modulePathIgnorePatterns="/canvas/storybook/"'); +} + +if (process.env.NODE_ENV == null) { + process.env.NODE_ENV = 'test'; +} + +require('jest').run(); diff --git a/x-pack/plugins/canvas/storybook/dll_contexts.js b/x-pack/plugins/canvas/storybook/dll_contexts.js index 02ceafd0b3983..8397f2f2e75f6 100644 --- a/x-pack/plugins/canvas/storybook/dll_contexts.js +++ b/x-pack/plugins/canvas/storybook/dll_contexts.js @@ -10,6 +10,3 @@ // eslint-disable-next-line @kbn/eslint/no-restricted-paths require('../../../../src/core/server/core_app/assets/legacy_light_theme.css'); - -const json = require.context('../shareable_runtime/test/workpads', false, /\.json$/); -json.keys().forEach((key) => json(key)); diff --git a/x-pack/plugins/case/jest.config.js b/x-pack/plugins/case/jest.config.js new file mode 100644 index 0000000000000..8095c70bc4a14 --- /dev/null +++ b/x-pack/plugins/case/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/case'], +}; diff --git a/x-pack/plugins/cloud/jest.config.js b/x-pack/plugins/cloud/jest.config.js new file mode 100644 index 0000000000000..e3844a97e5692 --- /dev/null +++ b/x-pack/plugins/cloud/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/cloud'], +}; diff --git a/x-pack/plugins/code/jest.config.js b/x-pack/plugins/code/jest.config.js new file mode 100644 index 0000000000000..2b2b078cc966c --- /dev/null +++ b/x-pack/plugins/code/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/code'], +}; diff --git a/x-pack/plugins/cross_cluster_replication/jest.config.js b/x-pack/plugins/cross_cluster_replication/jest.config.js new file mode 100644 index 0000000000000..6202a45413906 --- /dev/null +++ b/x-pack/plugins/cross_cluster_replication/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/cross_cluster_replication'], +}; diff --git a/x-pack/plugins/dashboard_enhanced/jest.config.js b/x-pack/plugins/dashboard_enhanced/jest.config.js new file mode 100644 index 0000000000000..5aeb423383c41 --- /dev/null +++ b/x-pack/plugins/dashboard_enhanced/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/dashboard_enhanced'], +}; diff --git a/x-pack/plugins/dashboard_mode/jest.config.js b/x-pack/plugins/dashboard_mode/jest.config.js new file mode 100644 index 0000000000000..062ad302da7c4 --- /dev/null +++ b/x-pack/plugins/dashboard_mode/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/dashboard_mode'], +}; diff --git a/x-pack/plugins/data_enhanced/jest.config.js b/x-pack/plugins/data_enhanced/jest.config.js new file mode 100644 index 0000000000000..b0b1e2d94b40a --- /dev/null +++ b/x-pack/plugins/data_enhanced/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/data_enhanced'], +}; diff --git a/x-pack/plugins/discover_enhanced/jest.config.js b/x-pack/plugins/discover_enhanced/jest.config.js new file mode 100644 index 0000000000000..00e040beba411 --- /dev/null +++ b/x-pack/plugins/discover_enhanced/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/discover_enhanced'], +}; diff --git a/x-pack/plugins/drilldowns/jest.config.js b/x-pack/plugins/drilldowns/jest.config.js new file mode 100644 index 0000000000000..a7d79f8dac378 --- /dev/null +++ b/x-pack/plugins/drilldowns/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/drilldowns'], +}; diff --git a/x-pack/plugins/embeddable_enhanced/jest.config.js b/x-pack/plugins/embeddable_enhanced/jest.config.js new file mode 100644 index 0000000000000..c5c62f98ca2f3 --- /dev/null +++ b/x-pack/plugins/embeddable_enhanced/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/embeddable_enhanced'], +}; diff --git a/x-pack/plugins/encrypted_saved_objects/jest.config.js b/x-pack/plugins/encrypted_saved_objects/jest.config.js new file mode 100644 index 0000000000000..0883bdb224dd0 --- /dev/null +++ b/x-pack/plugins/encrypted_saved_objects/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/encrypted_saved_objects'], +}; diff --git a/x-pack/plugins/enterprise_search/jest.config.js b/x-pack/plugins/enterprise_search/jest.config.js new file mode 100644 index 0000000000000..db6a25a1f7efd --- /dev/null +++ b/x-pack/plugins/enterprise_search/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/enterprise_search'], +}; diff --git a/x-pack/plugins/event_log/jest.config.js b/x-pack/plugins/event_log/jest.config.js new file mode 100644 index 0000000000000..bb847d3b3c7ce --- /dev/null +++ b/x-pack/plugins/event_log/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/event_log'], +}; diff --git a/x-pack/plugins/features/jest.config.js b/x-pack/plugins/features/jest.config.js new file mode 100644 index 0000000000000..e500d35bbbd60 --- /dev/null +++ b/x-pack/plugins/features/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/features'], +}; diff --git a/x-pack/plugins/file_upload/jest.config.js b/x-pack/plugins/file_upload/jest.config.js new file mode 100644 index 0000000000000..6a042a4cc5c1e --- /dev/null +++ b/x-pack/plugins/file_upload/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/file_upload'], +}; diff --git a/x-pack/plugins/fleet/jest.config.js b/x-pack/plugins/fleet/jest.config.js new file mode 100644 index 0000000000000..521cb7467b196 --- /dev/null +++ b/x-pack/plugins/fleet/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/fleet'], +}; diff --git a/x-pack/plugins/global_search/jest.config.js b/x-pack/plugins/global_search/jest.config.js new file mode 100644 index 0000000000000..2ad904d8c57c4 --- /dev/null +++ b/x-pack/plugins/global_search/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/global_search'], +}; diff --git a/x-pack/plugins/global_search_bar/jest.config.js b/x-pack/plugins/global_search_bar/jest.config.js new file mode 100644 index 0000000000000..5b03d4a3f90d7 --- /dev/null +++ b/x-pack/plugins/global_search_bar/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/global_search_bar'], +}; diff --git a/x-pack/plugins/global_search_providers/jest.config.js b/x-pack/plugins/global_search_providers/jest.config.js new file mode 100644 index 0000000000000..3bd70206c936c --- /dev/null +++ b/x-pack/plugins/global_search_providers/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/global_search_providers'], +}; diff --git a/x-pack/plugins/graph/jest.config.js b/x-pack/plugins/graph/jest.config.js new file mode 100644 index 0000000000000..da729b0fae223 --- /dev/null +++ b/x-pack/plugins/graph/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/graph'], +}; diff --git a/x-pack/plugins/index_lifecycle_management/jest.config.js b/x-pack/plugins/index_lifecycle_management/jest.config.js new file mode 100644 index 0000000000000..906f4ff3960ae --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/index_lifecycle_management'], +}; diff --git a/x-pack/plugins/index_management/jest.config.js b/x-pack/plugins/index_management/jest.config.js new file mode 100644 index 0000000000000..d389a91675210 --- /dev/null +++ b/x-pack/plugins/index_management/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/index_management'], +}; diff --git a/x-pack/plugins/infra/jest.config.js b/x-pack/plugins/infra/jest.config.js new file mode 100644 index 0000000000000..507f94a2d6685 --- /dev/null +++ b/x-pack/plugins/infra/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/infra'], +}; diff --git a/x-pack/plugins/ingest_manager/jest.config.js b/x-pack/plugins/ingest_manager/jest.config.js new file mode 100644 index 0000000000000..8aff85670176b --- /dev/null +++ b/x-pack/plugins/ingest_manager/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/ingest_manager'], +}; diff --git a/x-pack/plugins/ingest_pipelines/jest.config.js b/x-pack/plugins/ingest_pipelines/jest.config.js new file mode 100644 index 0000000000000..48ce7dea0b5ba --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/ingest_pipelines'], +}; diff --git a/x-pack/plugins/lens/jest.config.js b/x-pack/plugins/lens/jest.config.js new file mode 100644 index 0000000000000..bcb80519c5ef5 --- /dev/null +++ b/x-pack/plugins/lens/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/lens'], +}; diff --git a/x-pack/plugins/license_management/jest.config.js b/x-pack/plugins/license_management/jest.config.js new file mode 100644 index 0000000000000..593b8fce47411 --- /dev/null +++ b/x-pack/plugins/license_management/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/license_management'], +}; diff --git a/x-pack/plugins/licensing/jest.config.js b/x-pack/plugins/licensing/jest.config.js new file mode 100644 index 0000000000000..72f3fd90ae5e1 --- /dev/null +++ b/x-pack/plugins/licensing/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/licensing'], +}; diff --git a/x-pack/plugins/lists/jest.config.js b/x-pack/plugins/lists/jest.config.js new file mode 100644 index 0000000000000..4d933fa20ba76 --- /dev/null +++ b/x-pack/plugins/lists/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/lists'], +}; diff --git a/x-pack/plugins/logstash/jest.config.js b/x-pack/plugins/logstash/jest.config.js new file mode 100644 index 0000000000000..52e1d7b1a6693 --- /dev/null +++ b/x-pack/plugins/logstash/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/logstash'], +}; diff --git a/x-pack/plugins/maps/jest.config.js b/x-pack/plugins/maps/jest.config.js new file mode 100644 index 0000000000000..40dea38c6f2a1 --- /dev/null +++ b/x-pack/plugins/maps/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/maps'], +}; diff --git a/x-pack/plugins/ml/jest.config.js b/x-pack/plugins/ml/jest.config.js new file mode 100644 index 0000000000000..bd77ac77c5e97 --- /dev/null +++ b/x-pack/plugins/ml/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/ml'], +}; diff --git a/x-pack/plugins/monitoring/jest.config.js b/x-pack/plugins/monitoring/jest.config.js new file mode 100644 index 0000000000000..5979afd96b477 --- /dev/null +++ b/x-pack/plugins/monitoring/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/monitoring'], +}; diff --git a/x-pack/plugins/observability/jest.config.js b/x-pack/plugins/observability/jest.config.js index cbf9a86360b89..54bb6c96ddce9 100644 --- a/x-pack/plugins/observability/jest.config.js +++ b/x-pack/plugins/observability/jest.config.js @@ -4,37 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -// This is an APM-specific Jest configuration which overrides the x-pack -// configuration. It's intended for use in development and does not run in CI, -// which runs the entire x-pack suite. Run `npx jest`. - -require('../../../src/setup_node_env'); - -const { createJestConfig } = require('../../dev-tools/jest/create_jest_config'); -const { resolve } = require('path'); - -const rootDir = resolve(__dirname, '.'); -const xPackKibanaDirectory = resolve(__dirname, '../..'); -const kibanaDirectory = resolve(__dirname, '../../..'); - -const jestConfig = createJestConfig({ - kibanaDirectory, - rootDir, - xPackKibanaDirectory, -}); - module.exports = { - ...jestConfig, - reporters: ['default'], - roots: [`${rootDir}/common`, `${rootDir}/public`, `${rootDir}/server`], - collectCoverage: true, - collectCoverageFrom: [ - ...jestConfig.collectCoverageFrom, - '**/*.{js,mjs,jsx,ts,tsx}', - '!**/*.stories.{js,mjs,ts,tsx}', - '!**/target/**', - '!**/typings/**', - ], - coverageDirectory: `${rootDir}/target/coverage/jest`, - coverageReporters: ['html'], + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/observability'], }; diff --git a/x-pack/plugins/painless_lab/jest.config.js b/x-pack/plugins/painless_lab/jest.config.js new file mode 100644 index 0000000000000..9e0e0fe792285 --- /dev/null +++ b/x-pack/plugins/painless_lab/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/painless_lab'], +}; diff --git a/x-pack/plugins/remote_clusters/jest.config.js b/x-pack/plugins/remote_clusters/jest.config.js new file mode 100644 index 0000000000000..81728f99934bc --- /dev/null +++ b/x-pack/plugins/remote_clusters/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/remote_clusters'], +}; diff --git a/x-pack/plugins/reporting/jest.config.js b/x-pack/plugins/reporting/jest.config.js new file mode 100644 index 0000000000000..1faa533c09c7b --- /dev/null +++ b/x-pack/plugins/reporting/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/reporting'], +}; diff --git a/x-pack/plugins/rollup/jest.config.js b/x-pack/plugins/rollup/jest.config.js new file mode 100644 index 0000000000000..edb3ba860dc74 --- /dev/null +++ b/x-pack/plugins/rollup/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/rollup'], +}; diff --git a/x-pack/plugins/runtime_fields/jest.config.js b/x-pack/plugins/runtime_fields/jest.config.js new file mode 100644 index 0000000000000..9c4ec56593c8b --- /dev/null +++ b/x-pack/plugins/runtime_fields/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/runtime_fields'], +}; diff --git a/x-pack/plugins/saved_objects_tagging/jest.config.js b/x-pack/plugins/saved_objects_tagging/jest.config.js new file mode 100644 index 0000000000000..82931258a4055 --- /dev/null +++ b/x-pack/plugins/saved_objects_tagging/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/saved_objects_tagging'], +}; diff --git a/x-pack/plugins/searchprofiler/jest.config.js b/x-pack/plugins/searchprofiler/jest.config.js new file mode 100644 index 0000000000000..b80a9924c4fd2 --- /dev/null +++ b/x-pack/plugins/searchprofiler/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/searchprofiler'], +}; diff --git a/x-pack/plugins/security/jest.config.js b/x-pack/plugins/security/jest.config.js new file mode 100644 index 0000000000000..26fee5a787850 --- /dev/null +++ b/x-pack/plugins/security/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/security'], +}; diff --git a/x-pack/plugins/security_solution/jest.config.js b/x-pack/plugins/security_solution/jest.config.js new file mode 100644 index 0000000000000..ae7a2dbbd05ca --- /dev/null +++ b/x-pack/plugins/security_solution/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/security_solution'], +}; diff --git a/x-pack/plugins/snapshot_restore/jest.config.js b/x-pack/plugins/snapshot_restore/jest.config.js new file mode 100644 index 0000000000000..e485eff0fb355 --- /dev/null +++ b/x-pack/plugins/snapshot_restore/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/snapshot_restore'], +}; diff --git a/x-pack/plugins/spaces/jest.config.js b/x-pack/plugins/spaces/jest.config.js new file mode 100644 index 0000000000000..c3e7db9a4c7c3 --- /dev/null +++ b/x-pack/plugins/spaces/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/spaces'], +}; diff --git a/x-pack/plugins/stack_alerts/jest.config.js b/x-pack/plugins/stack_alerts/jest.config.js new file mode 100644 index 0000000000000..a34c1ad828e01 --- /dev/null +++ b/x-pack/plugins/stack_alerts/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/stack_alerts'], +}; diff --git a/x-pack/plugins/task_manager/jest.config.js b/x-pack/plugins/task_manager/jest.config.js new file mode 100644 index 0000000000000..6acb44700921b --- /dev/null +++ b/x-pack/plugins/task_manager/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/task_manager'], +}; diff --git a/x-pack/plugins/telemetry_collection_xpack/jest.config.js b/x-pack/plugins/telemetry_collection_xpack/jest.config.js new file mode 100644 index 0000000000000..341be31243db8 --- /dev/null +++ b/x-pack/plugins/telemetry_collection_xpack/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/telemetry_collection_xpack'], +}; diff --git a/x-pack/plugins/transform/jest.config.js b/x-pack/plugins/transform/jest.config.js new file mode 100644 index 0000000000000..b752d071e4909 --- /dev/null +++ b/x-pack/plugins/transform/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/transform'], +}; diff --git a/x-pack/plugins/triggers_actions_ui/jest.config.js b/x-pack/plugins/triggers_actions_ui/jest.config.js new file mode 100644 index 0000000000000..63f3b24da4f56 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/triggers_actions_ui'], +}; diff --git a/x-pack/plugins/ui_actions_enhanced/jest.config.js b/x-pack/plugins/ui_actions_enhanced/jest.config.js new file mode 100644 index 0000000000000..a68fc82413583 --- /dev/null +++ b/x-pack/plugins/ui_actions_enhanced/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/ui_actions_enhanced'], +}; diff --git a/x-pack/plugins/upgrade_assistant/jest.config.js b/x-pack/plugins/upgrade_assistant/jest.config.js new file mode 100644 index 0000000000000..ad0ea1741822c --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/upgrade_assistant'], +}; diff --git a/x-pack/plugins/uptime/jest.config.js b/x-pack/plugins/uptime/jest.config.js new file mode 100644 index 0000000000000..85da90927af17 --- /dev/null +++ b/x-pack/plugins/uptime/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/uptime'], +}; diff --git a/x-pack/plugins/vis_type_timeseries_enhanced/jest.config.js b/x-pack/plugins/vis_type_timeseries_enhanced/jest.config.js new file mode 100644 index 0000000000000..17c5c87e3ccc2 --- /dev/null +++ b/x-pack/plugins/vis_type_timeseries_enhanced/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/vis_type_timeseries_enhanced'], +}; diff --git a/x-pack/plugins/watcher/jest.config.js b/x-pack/plugins/watcher/jest.config.js new file mode 100644 index 0000000000000..11ddd8bedc80c --- /dev/null +++ b/x-pack/plugins/watcher/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/watcher'], +}; diff --git a/x-pack/plugins/xpack_legacy/jest.config.js b/x-pack/plugins/xpack_legacy/jest.config.js new file mode 100644 index 0000000000000..16126ca0fa567 --- /dev/null +++ b/x-pack/plugins/xpack_legacy/jest.config.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/xpack_legacy'], +}; diff --git a/x-pack/scripts/jest.js b/x-pack/scripts/jest.js index f807610fd60de..68cfcf082f818 100644 --- a/x-pack/scripts/jest.js +++ b/x-pack/scripts/jest.js @@ -4,5 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -require('../../src/setup_node_env'); -require('../dev-tools/jest').runJest(); +if (process.argv.indexOf('--config') === -1) { + // append correct jest.config if none is provided + const configPath = require('path').resolve(__dirname, '../jest.config.js'); + process.argv.push('--config', configPath); + console.log('Running Jest with --config', configPath); +} + +if (process.env.NODE_ENV == null) { + process.env.NODE_ENV = 'test'; +} + +require('jest').run(); diff --git a/x-pack/scripts/jest_integration.js b/x-pack/scripts/jest_integration.js deleted file mode 100644 index 8311a9d283cbd..0000000000000 --- a/x-pack/scripts/jest_integration.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -// # Run Jest integration tests -// -// All args will be forwarded directly to Jest, e.g. to watch tests run: -// -// node scripts/jest_integration --watch -// -// or to build code coverage: -// -// node scripts/jest_integration --coverage -// -// See all cli options in https://facebook.github.io/jest/docs/cli.html - -const resolve = require('path').resolve; -process.argv.push('--config', resolve(__dirname, '../test_utils/jest/config.integration.js')); -process.argv.push('--runInBand'); - -require('../../src/setup_node_env'); -require('../../src/dev/jest/cli'); diff --git a/x-pack/test/api_integration/apis/uptime/rest/__snapshots__/monitor_states_real_data.snap b/x-pack/test/api_integration/apis/uptime/rest/__snapshots__/monitor_states_real_data.snap deleted file mode 100644 index 93abfaf67a009..0000000000000 --- a/x-pack/test/api_integration/apis/uptime/rest/__snapshots__/monitor_states_real_data.snap +++ /dev/null @@ -1,371 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`monitor states endpoint will fetch monitor state data for the given down filters 1`] = ` -Object { - "nextPagePagination": "{\\"cursorDirection\\":\\"AFTER\\",\\"sortOrder\\":\\"ASC\\",\\"cursorKey\\":{\\"monitor_id\\":\\"0020-down\\"}}", - "prevPagePagination": null, - "summaries": Array [ - Object { - "histogram": Object { - "points": Array [ - Object { - "down": 1, - "timestamp": 1568172624744, - }, - Object { - "down": 2, - "timestamp": 1568172677247, - }, - Object { - "down": 1, - "timestamp": 1568172729750, - }, - Object { - "down": 2, - "timestamp": 1568172782253, - }, - Object { - "down": 2, - "timestamp": 1568172834756, - }, - Object { - "down": 2, - "timestamp": 1568172887259, - }, - Object { - "down": 1, - "timestamp": 1568172939762, - }, - Object { - "down": 2, - "timestamp": 1568172992265, - }, - Object { - "down": 2, - "timestamp": 1568173044768, - }, - Object { - "down": 2, - "timestamp": 1568173097271, - }, - Object { - "down": 1, - "timestamp": 1568173149774, - }, - Object { - "down": 2, - "timestamp": 1568173202277, - }, - ], - }, - "minInterval": 52503, - "monitor_id": "0010-down", - "state": Object { - "monitor": Object { - "name": "", - "type": "http", - }, - "observer": Object { - "geo": Object { - "name": Array [ - "mpls", - ], - }, - }, - "summary": Object { - "down": 1, - "status": "down", - "up": 0, - }, - "summaryPings": Array [ - Object { - "@timestamp": "2019-09-11T03:40:34.371Z", - "agent": Object { - "ephemeral_id": "412a92a8-2142-4b1a-a7a2-1afd32e12f85", - "hostname": "avc-x1x", - "id": "04e1d082-65bc-4929-8d65-d0768a2621c4", - "type": "heartbeat", - "version": "8.0.0", - }, - "docId": "rZtoHm0B0I9WX_CznN_V", - "ecs": Object { - "version": "1.1.0", - }, - "error": Object { - "message": "400 Bad Request", - "type": "validate", - }, - "event": Object { - "dataset": "uptime", - }, - "host": Object { - "name": "avc-x1x", - }, - "http": Object { - "response": Object { - "body": Object { - "bytes": 3, - "content": "400", - "hash": "26d228663f13a88592a12d16cf9587caab0388b262d6d9f126ed62f9333aca94", - }, - "status_code": 400, - }, - "rtt": Object { - "content": Object { - "us": 41, - }, - "response_header": Object { - "us": 36777, - }, - "total": Object { - "us": 37821, - }, - "validate": Object { - "us": 36818, - }, - "write_request": Object { - "us": 53, - }, - }, - }, - "monitor": Object { - "check_group": "d76f07d1-d445-11e9-88e3-3e80641b9c71", - "duration": Object { - "us": 37926, - }, - "id": "0010-down", - "ip": "127.0.0.1", - "name": "", - "status": "down", - "type": "http", - }, - "observer": Object { - "geo": Object { - "location": "37.926868, -78.024902", - "name": "mpls", - }, - "hostname": "avc-x1x", - }, - "resolve": Object { - "ip": "127.0.0.1", - "rtt": Object { - "us": 56, - }, - }, - "summary": Object { - "down": 1, - "up": 0, - }, - "tcp": Object { - "rtt": Object { - "connect": Object { - "us": 890, - }, - }, - }, - "timestamp": "2019-09-11T03:40:34.371Z", - "url": Object { - "domain": "localhost", - "full": "http://localhost:5678/pattern?r=400x1", - "path": "/pattern", - "port": 5678, - "query": "r=400x1", - "scheme": "http", - }, - }, - ], - "timestamp": "2019-09-11T03:40:34.371Z", - "url": Object { - "domain": "localhost", - "full": "http://localhost:5678/pattern?r=400x1", - "path": "/pattern", - "port": 5678, - "query": "r=400x1", - "scheme": "http", - }, - }, - }, - Object { - "histogram": Object { - "points": Array [ - Object { - "down": 1, - "timestamp": 1568172624744, - }, - Object { - "down": 2, - "timestamp": 1568172677247, - }, - Object { - "down": 1, - "timestamp": 1568172729750, - }, - Object { - "down": 2, - "timestamp": 1568172782253, - }, - Object { - "down": 2, - "timestamp": 1568172834756, - }, - Object { - "down": 2, - "timestamp": 1568172887259, - }, - Object { - "down": 1, - "timestamp": 1568172939762, - }, - Object { - "down": 2, - "timestamp": 1568172992265, - }, - Object { - "down": 2, - "timestamp": 1568173044768, - }, - Object { - "down": 2, - "timestamp": 1568173097271, - }, - Object { - "down": 1, - "timestamp": 1568173149774, - }, - Object { - "down": 2, - "timestamp": 1568173202277, - }, - ], - }, - "minInterval": 52503, - "monitor_id": "0020-down", - "state": Object { - "monitor": Object { - "name": "", - "type": "http", - }, - "observer": Object { - "geo": Object { - "name": Array [ - "mpls", - ], - }, - }, - "summary": Object { - "down": 1, - "status": "down", - "up": 0, - }, - "summaryPings": Array [ - Object { - "@timestamp": "2019-09-11T03:40:34.372Z", - "agent": Object { - "ephemeral_id": "412a92a8-2142-4b1a-a7a2-1afd32e12f85", - "hostname": "avc-x1x", - "id": "04e1d082-65bc-4929-8d65-d0768a2621c4", - "type": "heartbeat", - "version": "8.0.0", - }, - "docId": "X5toHm0B0I9WX_CznN-6", - "ecs": Object { - "version": "1.1.0", - }, - "error": Object { - "message": "400 Bad Request", - "type": "validate", - }, - "event": Object { - "dataset": "uptime", - }, - "host": Object { - "name": "avc-x1x", - }, - "http": Object { - "response": Object { - "body": Object { - "bytes": 3, - "content": "400", - "hash": "26d228663f13a88592a12d16cf9587caab0388b262d6d9f126ed62f9333aca94", - }, - "status_code": 400, - }, - "rtt": Object { - "content": Object { - "us": 54, - }, - "response_header": Object { - "us": 180, - }, - "total": Object { - "us": 555, - }, - "validate": Object { - "us": 234, - }, - "write_request": Object { - "us": 63, - }, - }, - }, - "monitor": Object { - "check_group": "d7712ecb-d445-11e9-88e3-3e80641b9c71", - "duration": Object { - "us": 14900, - }, - "id": "0020-down", - "ip": "127.0.0.1", - "name": "", - "status": "down", - "type": "http", - }, - "observer": Object { - "geo": Object { - "location": "37.926868, -78.024902", - "name": "mpls", - }, - "hostname": "avc-x1x", - }, - "resolve": Object { - "ip": "127.0.0.1", - "rtt": Object { - "us": 14294, - }, - }, - "summary": Object { - "down": 1, - "up": 0, - }, - "tcp": Object { - "rtt": Object { - "connect": Object { - "us": 105, - }, - }, - }, - "timestamp": "2019-09-11T03:40:34.372Z", - "url": Object { - "domain": "localhost", - "full": "http://localhost:5678/pattern?r=400x1", - "path": "/pattern", - "port": 5678, - "query": "r=400x1", - "scheme": "http", - }, - }, - ], - "timestamp": "2019-09-11T03:40:34.372Z", - "url": Object { - "domain": "localhost", - "full": "http://localhost:5678/pattern?r=400x1", - "path": "/pattern", - "port": 5678, - "query": "r=400x1", - "scheme": "http", - }, - }, - }, - ], - "totalSummaryCount": 2000, -} -`; diff --git a/x-pack/test/apm_api_integration/basic/tests/services/__snapshots__/throughput.snap b/x-pack/test/apm_api_integration/basic/tests/services/__snapshots__/throughput.snap deleted file mode 100644 index 434660cdc2c62..0000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/services/__snapshots__/throughput.snap +++ /dev/null @@ -1,250 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Throughput when data is loaded returns the service throughput has the correct throughput 1`] = ` -Array [ - Object { - "x": 1601389800000, - "y": 6, - }, - Object { - "x": 1601389830000, - "y": 0, - }, - Object { - "x": 1601389860000, - "y": 0, - }, - Object { - "x": 1601389890000, - "y": 0, - }, - Object { - "x": 1601389920000, - "y": 3, - }, - Object { - "x": 1601389950000, - "y": 1, - }, - Object { - "x": 1601389980000, - "y": 0, - }, - Object { - "x": 1601390010000, - "y": 0, - }, - Object { - "x": 1601390040000, - "y": 3, - }, - Object { - "x": 1601390070000, - "y": 2, - }, - Object { - "x": 1601390100000, - "y": 0, - }, - Object { - "x": 1601390130000, - "y": 0, - }, - Object { - "x": 1601390160000, - "y": 7, - }, - Object { - "x": 1601390190000, - "y": 3, - }, - Object { - "x": 1601390220000, - "y": 2, - }, - Object { - "x": 1601390250000, - "y": 0, - }, - Object { - "x": 1601390280000, - "y": 0, - }, - Object { - "x": 1601390310000, - "y": 8, - }, - Object { - "x": 1601390340000, - "y": 0, - }, - Object { - "x": 1601390370000, - "y": 0, - }, - Object { - "x": 1601390400000, - "y": 3, - }, - Object { - "x": 1601390430000, - "y": 0, - }, - Object { - "x": 1601390460000, - "y": 0, - }, - Object { - "x": 1601390490000, - "y": 0, - }, - Object { - "x": 1601390520000, - "y": 4, - }, - Object { - "x": 1601390550000, - "y": 3, - }, - Object { - "x": 1601390580000, - "y": 2, - }, - Object { - "x": 1601390610000, - "y": 0, - }, - Object { - "x": 1601390640000, - "y": 1, - }, - Object { - "x": 1601390670000, - "y": 2, - }, - Object { - "x": 1601390700000, - "y": 0, - }, - Object { - "x": 1601390730000, - "y": 0, - }, - Object { - "x": 1601390760000, - "y": 4, - }, - Object { - "x": 1601390790000, - "y": 1, - }, - Object { - "x": 1601390820000, - "y": 1, - }, - Object { - "x": 1601390850000, - "y": 0, - }, - Object { - "x": 1601390880000, - "y": 6, - }, - Object { - "x": 1601390910000, - "y": 0, - }, - Object { - "x": 1601390940000, - "y": 3, - }, - Object { - "x": 1601390970000, - "y": 0, - }, - Object { - "x": 1601391000000, - "y": 4, - }, - Object { - "x": 1601391030000, - "y": 0, - }, - Object { - "x": 1601391060000, - "y": 1, - }, - Object { - "x": 1601391090000, - "y": 0, - }, - Object { - "x": 1601391120000, - "y": 2, - }, - Object { - "x": 1601391150000, - "y": 1, - }, - Object { - "x": 1601391180000, - "y": 2, - }, - Object { - "x": 1601391210000, - "y": 0, - }, - Object { - "x": 1601391240000, - "y": 1, - }, - Object { - "x": 1601391270000, - "y": 0, - }, - Object { - "x": 1601391300000, - "y": 1, - }, - Object { - "x": 1601391330000, - "y": 0, - }, - Object { - "x": 1601391360000, - "y": 1, - }, - Object { - "x": 1601391390000, - "y": 0, - }, - Object { - "x": 1601391420000, - "y": 0, - }, - Object { - "x": 1601391450000, - "y": 0, - }, - Object { - "x": 1601391480000, - "y": 10, - }, - Object { - "x": 1601391510000, - "y": 3, - }, - Object { - "x": 1601391540000, - "y": 1, - }, - Object { - "x": 1601391570000, - "y": 0, - }, - Object { - "x": 1601391600000, - "y": 0, - }, -] -`; diff --git a/x-pack/test/apm_api_integration/basic/tests/traces/__snapshots__/top_traces.snap b/x-pack/test/apm_api_integration/basic/tests/traces/__snapshots__/top_traces.snap deleted file mode 100644 index 9cecb0b3b1dd7..0000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/traces/__snapshots__/top_traces.snap +++ /dev/null @@ -1,774 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Top traces when data is loaded returns the correct buckets 1`] = ` -Array [ - Object { - "averageResponseTime": 1756, - "impact": 0, - "key": Object { - "service.name": "opbeans-java", - "transaction.name": "DispatcherServlet#doPost", - }, - "serviceName": "opbeans-java", - "transactionName": "DispatcherServlet#doPost", - "transactionType": "request", - "transactionsPerMinute": 0.0333333333333333, - }, - Object { - "averageResponseTime": 3251, - "impact": 0.00224063647384788, - "key": Object { - "service.name": "opbeans-node", - "transaction.name": "GET /api/types", - }, - "serviceName": "opbeans-node", - "transactionName": "GET /api/types", - "transactionType": "request", - "transactionsPerMinute": 0.0333333333333333, - }, - Object { - "averageResponseTime": 3813, - "impact": 0.00308293593759538, - "key": Object { - "service.name": "opbeans-java", - "transaction.name": "ResourceHttpRequestHandler", - }, - "serviceName": "opbeans-java", - "transactionName": "ResourceHttpRequestHandler", - "transactionType": "request", - "transactionsPerMinute": 0.0333333333333333, - }, - Object { - "averageResponseTime": 7741, - "impact": 0.0089700396628626, - "key": Object { - "service.name": "opbeans-node", - "transaction.name": "GET /api/products/top", - }, - "serviceName": "opbeans-node", - "transactionName": "GET /api/products/top", - "transactionType": "request", - "transactionsPerMinute": 0.0333333333333333, - }, - Object { - "averageResponseTime": 7994, - "impact": 0.00934922429689839, - "key": Object { - "service.name": "opbeans-go", - "transaction.name": "POST /api/orders", - }, - "serviceName": "opbeans-go", - "transactionName": "POST /api/orders", - "transactionType": "request", - "transactionsPerMinute": 0.0333333333333333, - }, - Object { - "averageResponseTime": 10317, - "impact": 0.0128308286639543, - "key": Object { - "service.name": "opbeans-node", - "transaction.name": "GET /api/orders/:id", - }, - "serviceName": "opbeans-node", - "transactionName": "GET /api/orders/:id", - "transactionType": "request", - "transactionsPerMinute": 0.0333333333333333, - }, - Object { - "averageResponseTime": 10837, - "impact": 0.0136101804809449, - "key": Object { - "service.name": "opbeans-java", - "transaction.name": "APIRestController#topProducts", - }, - "serviceName": "opbeans-java", - "transactionName": "APIRestController#topProducts", - "transactionType": "request", - "transactionsPerMinute": 0.0333333333333333, - }, - Object { - "averageResponseTime": 6495, - "impact": 0.0168369967539847, - "key": Object { - "service.name": "opbeans-node", - "transaction.name": "GET /api/products/:id", - }, - "serviceName": "opbeans-node", - "transactionName": "GET /api/products/:id", - "transactionType": "request", - "transactionsPerMinute": 0.0666666666666667, - }, - Object { - "averageResponseTime": 13952, - "impact": 0.0182787976154172, - "key": Object { - "service.name": "opbeans-java", - "transaction.name": "APIRestController#stats", - }, - "serviceName": "opbeans-java", - "transactionName": "APIRestController#stats", - "transactionType": "request", - "transactionsPerMinute": 0.0333333333333333, - }, - Object { - "averageResponseTime": 7324.5, - "impact": 0.0193234288008834, - "key": Object { - "service.name": "opbeans-java", - "transaction.name": "APIRestController#customerWhoBought", - }, - "serviceName": "opbeans-java", - "transactionName": "APIRestController#customerWhoBought", - "transactionType": "request", - "transactionsPerMinute": 0.0666666666666667, - }, - Object { - "averageResponseTime": 7089.66666666667, - "impact": 0.0292451769325711, - "key": Object { - "service.name": "opbeans-go", - "transaction.name": "GET /api/customers/:id", - }, - "serviceName": "opbeans-go", - "transactionName": "GET /api/customers/:id", - "transactionType": "request", - "transactionsPerMinute": 0.1, - }, - Object { - "averageResponseTime": 11759.5, - "impact": 0.0326173722945495, - "key": Object { - "service.name": "opbeans-node", - "transaction.name": "GET /api/customers/:id", - }, - "serviceName": "opbeans-node", - "transactionName": "GET /api/customers/:id", - "transactionType": "request", - "transactionsPerMinute": 0.0666666666666667, - }, - Object { - "averageResponseTime": 8109.33333333333, - "impact": 0.0338298638713675, - "key": Object { - "service.name": "opbeans-java", - "transaction.name": "APIRestController#customer", - }, - "serviceName": "opbeans-java", - "transactionName": "APIRestController#customer", - "transactionType": "request", - "transactionsPerMinute": 0.1, - }, - Object { - "averageResponseTime": 8677.33333333333, - "impact": 0.0363837398255058, - "key": Object { - "service.name": "opbeans-java", - "transaction.name": "APIRestController#order", - }, - "serviceName": "opbeans-java", - "transactionName": "APIRestController#order", - "transactionType": "request", - "transactionsPerMinute": 0.1, - }, - Object { - "averageResponseTime": 26624, - "impact": 0.0372710018940797, - "key": Object { - "service.name": "opbeans-node", - "transaction.name": "GET /api/customers", - }, - "serviceName": "opbeans-node", - "transactionName": "GET /api/customers", - "transactionType": "request", - "transactionsPerMinute": 0.0333333333333333, - }, - Object { - "averageResponseTime": 5687.8, - "impact": 0.0399912394860756, - "key": Object { - "service.name": "opbeans-go", - "transaction.name": "GET /api/products", - }, - "serviceName": "opbeans-go", - "transactionName": "GET /api/products", - "transactionType": "request", - "transactionsPerMinute": 0.166666666666667, - }, - Object { - "averageResponseTime": 9496.33333333333, - "impact": 0.0400661771607863, - "key": Object { - "service.name": "opbeans-node", - "transaction.name": "GET /api/products", - }, - "serviceName": "opbeans-node", - "transactionName": "GET /api/products", - "transactionType": "request", - "transactionsPerMinute": 0.1, - }, - Object { - "averageResponseTime": 10717.3333333333, - "impact": 0.0455561112100871, - "key": Object { - "service.name": "opbeans-java", - "transaction.name": "APIRestController#products", - }, - "serviceName": "opbeans-java", - "transactionName": "APIRestController#products", - "transactionType": "request", - "transactionsPerMinute": 0.1, - }, - Object { - "averageResponseTime": 8438.75, - "impact": 0.04795861306131, - "key": Object { - "service.name": "opbeans-node", - "transaction.name": "GET /api/orders", - }, - "serviceName": "opbeans-node", - "transactionName": "GET /api/orders", - "transactionType": "request", - "transactionsPerMinute": 0.133333333333333, - }, - Object { - "averageResponseTime": 17322.5, - "impact": 0.0492925036711592, - "key": Object { - "service.name": "opbeans-java", - "transaction.name": "APIRestController#customers", - }, - "serviceName": "opbeans-java", - "transactionName": "APIRestController#customers", - "transactionType": "request", - "transactionsPerMinute": 0.0666666666666667, - }, - Object { - "averageResponseTime": 34696, - "impact": 0.0493689400993641, - "key": Object { - "service.name": "opbeans-python", - "transaction.name": "GET opbeans.views.product", - }, - "serviceName": "opbeans-python", - "transactionName": "GET opbeans.views.product", - "transactionType": "request", - "transactionsPerMinute": 0.0333333333333333, - }, - Object { - "averageResponseTime": 7321.4, - "impact": 0.0522330580268044, - "key": Object { - "service.name": "opbeans-go", - "transaction.name": "GET /api/types/:id", - }, - "serviceName": "opbeans-go", - "transactionName": "GET /api/types/:id", - "transactionType": "request", - "transactionsPerMinute": 0.166666666666667, - }, - Object { - "averageResponseTime": 9663.5, - "impact": 0.0553010064294577, - "key": Object { - "service.name": "opbeans-ruby", - "transaction.name": "Api::OrdersController#show", - }, - "serviceName": "opbeans-ruby", - "transactionName": "Api::OrdersController#show", - "transactionType": "request", - "transactionsPerMinute": 0.133333333333333, - }, - Object { - "averageResponseTime": 44819, - "impact": 0.0645408217212785, - "key": Object { - "service.name": "opbeans-python", - "transaction.name": "GET opbeans.views.products", - }, - "serviceName": "opbeans-python", - "transactionName": "GET opbeans.views.products", - "transactionType": "request", - "transactionsPerMinute": 0.0333333333333333, - }, - Object { - "averageResponseTime": 14944, - "impact": 0.0645603055167033, - "key": Object { - "service.name": "opbeans-ruby", - "transaction.name": "Api::ProductsController#index", - }, - "serviceName": "opbeans-ruby", - "transactionName": "Api::ProductsController#index", - "transactionType": "request", - "transactionsPerMinute": 0.1, - }, - Object { - "averageResponseTime": 24056, - "impact": 0.0694762169777207, - "key": Object { - "service.name": "opbeans-python", - "transaction.name": "GET opbeans.views.product_types", - }, - "serviceName": "opbeans-python", - "transactionName": "GET opbeans.views.product_types", - "transactionType": "request", - "transactionsPerMinute": 0.0666666666666667, - }, - Object { - "averageResponseTime": 8401.33333333333, - "impact": 0.0729173550004329, - "key": Object { - "service.name": "opbeans-go", - "transaction.name": "GET /api/types", - }, - "serviceName": "opbeans-go", - "transactionName": "GET /api/types", - "transactionType": "request", - "transactionsPerMinute": 0.2, - }, - Object { - "averageResponseTime": 13182, - "impact": 0.0763944631070062, - "key": Object { - "service.name": "opbeans-node", - "transaction.name": "GET /api/products/:id/customers", - }, - "serviceName": "opbeans-node", - "transactionName": "GET /api/products/:id/customers", - "transactionType": "request", - "transactionsPerMinute": 0.133333333333333, - }, - Object { - "averageResponseTime": 7923, - "impact": 0.0804905564066893, - "key": Object { - "service.name": "opbeans-ruby", - "transaction.name": "Api::TypesController#index", - }, - "serviceName": "opbeans-ruby", - "transactionName": "Api::TypesController#index", - "transactionType": "request", - "transactionsPerMinute": 0.233333333333333, - }, - Object { - "averageResponseTime": 19838.6666666667, - "impact": 0.0865680018257216, - "key": Object { - "service.name": "opbeans-ruby", - "transaction.name": "Api::CustomersController#index", - }, - "serviceName": "opbeans-ruby", - "transactionName": "Api::CustomersController#index", - "transactionType": "request", - "transactionsPerMinute": 0.1, - }, - Object { - "averageResponseTime": 7952.33333333333, - "impact": 0.104635475198455, - "key": Object { - "service.name": "opbeans-go", - "transaction.name": "GET /api/orders/:id", - }, - "serviceName": "opbeans-go", - "transactionName": "GET /api/orders/:id", - "transactionType": "request", - "transactionsPerMinute": 0.3, - }, - Object { - "averageResponseTime": 19666, - "impact": 0.115266133732905, - "key": Object { - "service.name": "opbeans-node", - "transaction.name": "GET /api/stats", - }, - "serviceName": "opbeans-node", - "transactionName": "GET /api/stats", - "transactionType": "request", - "transactionsPerMinute": 0.133333333333333, - }, - Object { - "averageResponseTime": 40188.5, - "impact": 0.117833498468491, - "key": Object { - "service.name": "opbeans-python", - "transaction.name": "GET opbeans.views.customer", - }, - "serviceName": "opbeans-python", - "transactionName": "GET opbeans.views.customer", - "transactionType": "request", - "transactionsPerMinute": 0.0666666666666667, - }, - Object { - "averageResponseTime": 26802.3333333333, - "impact": 0.117878461073318, - "key": Object { - "service.name": "opbeans-ruby", - "transaction.name": "Api::ProductsController#show", - }, - "serviceName": "opbeans-ruby", - "transactionName": "Api::ProductsController#show", - "transactionType": "request", - "transactionsPerMinute": 0.1, - }, - Object { - "averageResponseTime": 14709.3333333333, - "impact": 0.129642177249393, - "key": Object { - "service.name": "opbeans-ruby", - "transaction.name": "Api::StatsController#index", - }, - "serviceName": "opbeans-ruby", - "transactionName": "Api::StatsController#index", - "transactionType": "request", - "transactionsPerMinute": 0.2, - }, - Object { - "averageResponseTime": 15432, - "impact": 0.136140772400299, - "key": Object { - "service.name": "opbeans-ruby", - "transaction.name": "Api::TypesController#show", - }, - "serviceName": "opbeans-ruby", - "transactionName": "Api::TypesController#show", - "transactionType": "request", - "transactionsPerMinute": 0.2, - }, - Object { - "averageResponseTime": 33266.3333333333, - "impact": 0.146942288833089, - "key": Object { - "service.name": "opbeans-python", - "transaction.name": "GET opbeans.views.orders", - }, - "serviceName": "opbeans-python", - "transactionName": "GET opbeans.views.orders", - "transactionType": "request", - "transactionsPerMinute": 0.1, - }, - Object { - "averageResponseTime": 33445.3333333333, - "impact": 0.147747119459481, - "key": Object { - "service.name": "opbeans-python", - "transaction.name": "GET opbeans.views.customers", - }, - "serviceName": "opbeans-python", - "transactionName": "GET opbeans.views.customers", - "transactionType": "request", - "transactionsPerMinute": 0.1, - }, - Object { - "averageResponseTime": 107438, - "impact": 0.158391266775379, - "key": Object { - "service.name": "opbeans-python", - "transaction.name": "GET opbeans.views.top_products", - }, - "serviceName": "opbeans-python", - "transactionName": "GET opbeans.views.top_products", - "transactionType": "request", - "transactionsPerMinute": 0.0333333333333333, - }, - Object { - "averageResponseTime": 27696.75, - "impact": 0.163410592227497, - "key": Object { - "service.name": "opbeans-ruby", - "transaction.name": "Api::ProductsController#top", - }, - "serviceName": "opbeans-ruby", - "transactionName": "Api::ProductsController#top", - "transactionType": "request", - "transactionsPerMinute": 0.133333333333333, - }, - Object { - "averageResponseTime": 55832.5, - "impact": 0.164726497795416, - "key": Object { - "service.name": "opbeans-python", - "transaction.name": "GET opbeans.views.stats", - }, - "serviceName": "opbeans-python", - "transactionName": "GET opbeans.views.stats", - "transactionType": "request", - "transactionsPerMinute": 0.0666666666666667, - }, - Object { - "averageResponseTime": 10483.6363636364, - "impact": 0.170204441816763, - "key": Object { - "service.name": "opbeans-python", - "transaction.name": "GET opbeans.views.order", - }, - "serviceName": "opbeans-python", - "transactionName": "GET opbeans.views.order", - "transactionType": "request", - "transactionsPerMinute": 0.366666666666667, - }, - Object { - "averageResponseTime": 24524.5, - "impact": 0.217905269277069, - "key": Object { - "service.name": "opbeans-go", - "transaction.name": "GET /api/customers", - }, - "serviceName": "opbeans-go", - "transactionName": "GET /api/customers", - "transactionType": "request", - "transactionsPerMinute": 0.2, - }, - Object { - "averageResponseTime": 14822.3, - "impact": 0.219517928036841, - "key": Object { - "service.name": "opbeans-ruby", - "transaction.name": "Api::CustomersController#show", - }, - "serviceName": "opbeans-ruby", - "transactionName": "Api::CustomersController#show", - "transactionType": "request", - "transactionsPerMinute": 0.333333333333333, - }, - Object { - "averageResponseTime": 44771.75, - "impact": 0.26577545588222, - "key": Object { - "service.name": "opbeans-go", - "transaction.name": "GET /api/stats", - }, - "serviceName": "opbeans-go", - "transactionName": "GET /api/stats", - "transactionType": "request", - "transactionsPerMinute": 0.133333333333333, - }, - Object { - "averageResponseTime": 39421.4285714286, - "impact": 0.410949215592138, - "key": Object { - "service.name": "opbeans-ruby", - "transaction.name": "Api::OrdersController#index", - }, - "serviceName": "opbeans-ruby", - "transactionName": "Api::OrdersController#index", - "transactionType": "request", - "transactionsPerMinute": 0.233333333333333, - }, - Object { - "averageResponseTime": 33513.3076923077, - "impact": 0.650334619948262, - "key": Object { - "service.name": "opbeans-go", - "transaction.name": "GET /api/products/:id", - }, - "serviceName": "opbeans-go", - "transactionName": "GET /api/products/:id", - "transactionType": "request", - "transactionsPerMinute": 0.433333333333333, - }, - Object { - "averageResponseTime": 28933.2222222222, - "impact": 0.777916011143112, - "key": Object { - "service.name": "opbeans-node", - "transaction.name": "GET /api", - }, - "serviceName": "opbeans-node", - "transactionName": "GET /api", - "transactionType": "request", - "transactionsPerMinute": 0.6, - }, - Object { - "averageResponseTime": 101613, - "impact": 1.06341806051616, - "key": Object { - "service.name": "opbeans-go", - "transaction.name": "GET /api/products/:id/customers", - }, - "serviceName": "opbeans-go", - "transactionName": "GET /api/products/:id/customers", - "transactionType": "request", - "transactionsPerMinute": 0.233333333333333, - }, - Object { - "averageResponseTime": 377325, - "impact": 1.12840251327172, - "key": Object { - "service.name": "opbeans-python", - "transaction.name": "GET opbeans.views.product_customers", - }, - "serviceName": "opbeans-python", - "transactionName": "GET opbeans.views.product_customers", - "transactionType": "request", - "transactionsPerMinute": 0.0666666666666667, - }, - Object { - "averageResponseTime": 39452.8333333333, - "impact": 3.54517249775948, - "key": Object { - "service.name": "opbeans-python", - "transaction.name": "opbeans.tasks.sync_orders", - }, - "serviceName": "opbeans-python", - "transactionName": "opbeans.tasks.sync_orders", - "transactionType": "celery", - "transactionsPerMinute": 2, - }, - Object { - "averageResponseTime": 715444.444444444, - "impact": 9.64784193809929, - "key": Object { - "service.name": "opbeans-rum", - "transaction.name": "/customers", - }, - "serviceName": "opbeans-rum", - "transactionName": "/customers", - "transactionType": "page-load", - "transactionsPerMinute": 0.3, - }, - Object { - "averageResponseTime": 833539.125, - "impact": 9.99152559811767, - "key": Object { - "service.name": "opbeans-go", - "transaction.name": "GET /api/orders", - }, - "serviceName": "opbeans-go", - "transactionName": "GET /api/orders", - "transactionType": "request", - "transactionsPerMinute": 0.266666666666667, - }, - Object { - "averageResponseTime": 7480000, - "impact": 11.2080443255746, - "key": Object { - "service.name": "elastic-co-frontend", - "transaction.name": "/community/security", - }, - "serviceName": "elastic-co-frontend", - "transactionName": "/community/security", - "transactionType": "page-load", - "transactionsPerMinute": 0.0333333333333333, - }, - Object { - "averageResponseTime": 171383.519230769, - "impact": 13.354173900338, - "key": Object { - "service.name": "opbeans-ruby", - "transaction.name": "Rack", - }, - "serviceName": "opbeans-ruby", - "transactionName": "Rack", - "transactionType": "request", - "transactionsPerMinute": 1.73333333333333, - }, - Object { - "averageResponseTime": 1052468.6, - "impact": 15.7712781068549, - "key": Object { - "service.name": "opbeans-java", - "transaction.name": "DispatcherServlet#doGet", - }, - "serviceName": "opbeans-java", - "transactionName": "DispatcherServlet#doGet", - "transactionType": "request", - "transactionsPerMinute": 0.333333333333333, - }, - Object { - "averageResponseTime": 1413866.66666667, - "impact": 31.7829322941256, - "key": Object { - "service.name": "opbeans-rum", - "transaction.name": "/products", - }, - "serviceName": "opbeans-rum", - "transactionName": "/products", - "transactionType": "page-load", - "transactionsPerMinute": 0.5, - }, - Object { - "averageResponseTime": 996583.333333333, - "impact": 35.8445542634419, - "key": Object { - "service.name": "opbeans-rum", - "transaction.name": "/dashboard", - }, - "serviceName": "opbeans-rum", - "transactionName": "/dashboard", - "transactionType": "page-load", - "transactionsPerMinute": 0.8, - }, - Object { - "averageResponseTime": 1046912.60465116, - "impact": 67.4671169361798, - "key": Object { - "service.name": "opbeans-node", - "transaction.name": "Process completed order", - }, - "serviceName": "opbeans-node", - "transactionName": "Process completed order", - "transactionType": "Worker", - "transactionsPerMinute": 1.43333333333333, - }, - Object { - "averageResponseTime": 1142941.8, - "impact": 68.5168888461311, - "key": Object { - "service.name": "opbeans-node", - "transaction.name": "Update shipping status", - }, - "serviceName": "opbeans-node", - "transactionName": "Update shipping status", - "transactionType": "Worker", - "transactionsPerMinute": 1.33333333333333, - }, - Object { - "averageResponseTime": 128285.213888889, - "impact": 69.2138167147075, - "key": Object { - "service.name": "opbeans-python", - "transaction.name": "opbeans.tasks.update_stats", - }, - "serviceName": "opbeans-python", - "transactionName": "opbeans.tasks.update_stats", - "transactionType": "celery", - "transactionsPerMinute": 12, - }, - Object { - "averageResponseTime": 1032979.06666667, - "impact": 69.6655125415468, - "key": Object { - "service.name": "opbeans-node", - "transaction.name": "Process payment", - }, - "serviceName": "opbeans-node", - "transactionName": "Process payment", - "transactionType": "Worker", - "transactionsPerMinute": 1.5, - }, - Object { - "averageResponseTime": 4410285.71428571, - "impact": 92.5364039355288, - "key": Object { - "service.name": "opbeans-rum", - "transaction.name": "/orders", - }, - "serviceName": "opbeans-rum", - "transactionName": "/orders", - "transactionType": "page-load", - "transactionsPerMinute": 0.466666666666667, - }, - Object { - "averageResponseTime": 1803347.81081081, - "impact": 100, - "key": Object { - "service.name": "opbeans-python", - "transaction.name": "opbeans.tasks.sync_customers", - }, - "serviceName": "opbeans-python", - "transactionName": "opbeans.tasks.sync_customers", - "transactionType": "celery", - "transactionsPerMinute": 1.23333333333333, - }, -] -`; diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/breakdown.snap b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/breakdown.snap deleted file mode 100644 index 5f598ba72cd72..0000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/breakdown.snap +++ /dev/null @@ -1,1016 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Breakdown when data is loaded returns the transaction breakdown for a service 1`] = ` -Object { - "timeseries": Array [ - Object { - "color": "#54b399", - "data": Array [ - Object { - "x": 1601389800000, - "y": 0.0161290322580645, - }, - Object { - "x": 1601389830000, - "y": 0.402597402597403, - }, - Object { - "x": 1601389860000, - "y": 0.0303030303030303, - }, - Object { - "x": 1601389890000, - "y": null, - }, - Object { - "x": 1601389920000, - "y": 0.518072289156627, - }, - Object { - "x": 1601389950000, - "y": 0.120603015075377, - }, - Object { - "x": 1601389980000, - "y": 0.823529411764706, - }, - Object { - "x": 1601390010000, - "y": null, - }, - Object { - "x": 1601390040000, - "y": 0.273381294964029, - }, - Object { - "x": 1601390070000, - "y": 0.39047619047619, - }, - Object { - "x": 1601390100000, - "y": null, - }, - Object { - "x": 1601390130000, - "y": 0.733333333333333, - }, - Object { - "x": 1601390160000, - "y": 0.144230769230769, - }, - Object { - "x": 1601390190000, - "y": 0.0688524590163934, - }, - Object { - "x": 1601390220000, - "y": null, - }, - Object { - "x": 1601390250000, - "y": null, - }, - Object { - "x": 1601390280000, - "y": 0.0540540540540541, - }, - Object { - "x": 1601390310000, - "y": null, - }, - Object { - "x": 1601390340000, - "y": null, - }, - Object { - "x": 1601390370000, - "y": 1, - }, - Object { - "x": 1601390400000, - "y": null, - }, - Object { - "x": 1601390430000, - "y": 0.75, - }, - Object { - "x": 1601390460000, - "y": 0.764705882352941, - }, - Object { - "x": 1601390490000, - "y": 0.117647058823529, - }, - Object { - "x": 1601390520000, - "y": 0.220588235294118, - }, - Object { - "x": 1601390550000, - "y": 0.302325581395349, - }, - Object { - "x": 1601390580000, - "y": null, - }, - Object { - "x": 1601390610000, - "y": null, - }, - Object { - "x": 1601390640000, - "y": null, - }, - Object { - "x": 1601390670000, - "y": 0.215686274509804, - }, - Object { - "x": 1601390700000, - "y": null, - }, - Object { - "x": 1601390730000, - "y": null, - }, - Object { - "x": 1601390760000, - "y": 0.217391304347826, - }, - Object { - "x": 1601390790000, - "y": 0.253333333333333, - }, - Object { - "x": 1601390820000, - "y": null, - }, - Object { - "x": 1601390850000, - "y": 0.117647058823529, - }, - Object { - "x": 1601390880000, - "y": 0.361111111111111, - }, - Object { - "x": 1601390910000, - "y": null, - }, - Object { - "x": 1601390940000, - "y": null, - }, - Object { - "x": 1601390970000, - "y": 0.19047619047619, - }, - Object { - "x": 1601391000000, - "y": 0.354430379746835, - }, - Object { - "x": 1601391030000, - "y": null, - }, - Object { - "x": 1601391060000, - "y": null, - }, - Object { - "x": 1601391090000, - "y": null, - }, - Object { - "x": 1601391120000, - "y": 0.437956204379562, - }, - Object { - "x": 1601391150000, - "y": 0.0175438596491228, - }, - Object { - "x": 1601391180000, - "y": null, - }, - Object { - "x": 1601391210000, - "y": 0.277777777777778, - }, - Object { - "x": 1601391240000, - "y": 1, - }, - Object { - "x": 1601391270000, - "y": 0.885714285714286, - }, - Object { - "x": 1601391300000, - "y": null, - }, - Object { - "x": 1601391330000, - "y": null, - }, - Object { - "x": 1601391360000, - "y": 0.111111111111111, - }, - Object { - "x": 1601391390000, - "y": null, - }, - Object { - "x": 1601391420000, - "y": 0.764705882352941, - }, - Object { - "x": 1601391450000, - "y": null, - }, - Object { - "x": 1601391480000, - "y": 0.0338983050847458, - }, - Object { - "x": 1601391510000, - "y": 0.293233082706767, - }, - Object { - "x": 1601391540000, - "y": null, - }, - Object { - "x": 1601391570000, - "y": null, - }, - Object { - "x": 1601391600000, - "y": null, - }, - ], - "hideLegend": false, - "legendValue": "25%", - "title": "app", - "type": "areaStacked", - }, - Object { - "color": "#6092c0", - "data": Array [ - Object { - "x": 1601389800000, - "y": 0.983870967741935, - }, - Object { - "x": 1601389830000, - "y": 0.545454545454545, - }, - Object { - "x": 1601389860000, - "y": 0.96969696969697, - }, - Object { - "x": 1601389890000, - "y": null, - }, - Object { - "x": 1601389920000, - "y": 0.156626506024096, - }, - Object { - "x": 1601389950000, - "y": 0.85929648241206, - }, - Object { - "x": 1601389980000, - "y": 0, - }, - Object { - "x": 1601390010000, - "y": null, - }, - Object { - "x": 1601390040000, - "y": 0.482014388489209, - }, - Object { - "x": 1601390070000, - "y": 0.361904761904762, - }, - Object { - "x": 1601390100000, - "y": null, - }, - Object { - "x": 1601390130000, - "y": 0, - }, - Object { - "x": 1601390160000, - "y": 0.759615384615385, - }, - Object { - "x": 1601390190000, - "y": 0.931147540983607, - }, - Object { - "x": 1601390220000, - "y": null, - }, - Object { - "x": 1601390250000, - "y": null, - }, - Object { - "x": 1601390280000, - "y": 0.945945945945946, - }, - Object { - "x": 1601390310000, - "y": null, - }, - Object { - "x": 1601390340000, - "y": null, - }, - Object { - "x": 1601390370000, - "y": 0, - }, - Object { - "x": 1601390400000, - "y": null, - }, - Object { - "x": 1601390430000, - "y": 0, - }, - Object { - "x": 1601390460000, - "y": 0, - }, - Object { - "x": 1601390490000, - "y": 0.784313725490196, - }, - Object { - "x": 1601390520000, - "y": 0.544117647058823, - }, - Object { - "x": 1601390550000, - "y": 0.558139534883721, - }, - Object { - "x": 1601390580000, - "y": null, - }, - Object { - "x": 1601390610000, - "y": null, - }, - Object { - "x": 1601390640000, - "y": null, - }, - Object { - "x": 1601390670000, - "y": 0.784313725490196, - }, - Object { - "x": 1601390700000, - "y": null, - }, - Object { - "x": 1601390730000, - "y": null, - }, - Object { - "x": 1601390760000, - "y": 0.536231884057971, - }, - Object { - "x": 1601390790000, - "y": 0.746666666666667, - }, - Object { - "x": 1601390820000, - "y": null, - }, - Object { - "x": 1601390850000, - "y": 0.735294117647059, - }, - Object { - "x": 1601390880000, - "y": 0.416666666666667, - }, - Object { - "x": 1601390910000, - "y": null, - }, - Object { - "x": 1601390940000, - "y": null, - }, - Object { - "x": 1601390970000, - "y": 0.619047619047619, - }, - Object { - "x": 1601391000000, - "y": 0.518987341772152, - }, - Object { - "x": 1601391030000, - "y": null, - }, - Object { - "x": 1601391060000, - "y": null, - }, - Object { - "x": 1601391090000, - "y": null, - }, - Object { - "x": 1601391120000, - "y": 0.408759124087591, - }, - Object { - "x": 1601391150000, - "y": 0.982456140350877, - }, - Object { - "x": 1601391180000, - "y": null, - }, - Object { - "x": 1601391210000, - "y": 0.648148148148148, - }, - Object { - "x": 1601391240000, - "y": 0, - }, - Object { - "x": 1601391270000, - "y": 0, - }, - Object { - "x": 1601391300000, - "y": null, - }, - Object { - "x": 1601391330000, - "y": null, - }, - Object { - "x": 1601391360000, - "y": 0.888888888888889, - }, - Object { - "x": 1601391390000, - "y": null, - }, - Object { - "x": 1601391420000, - "y": 0, - }, - Object { - "x": 1601391450000, - "y": null, - }, - Object { - "x": 1601391480000, - "y": 0.966101694915254, - }, - Object { - "x": 1601391510000, - "y": 0.676691729323308, - }, - Object { - "x": 1601391540000, - "y": null, - }, - Object { - "x": 1601391570000, - "y": null, - }, - Object { - "x": 1601391600000, - "y": null, - }, - ], - "hideLegend": false, - "legendValue": "65%", - "title": "http", - "type": "areaStacked", - }, - Object { - "color": "#d36086", - "data": Array [ - Object { - "x": 1601389800000, - "y": 0, - }, - Object { - "x": 1601389830000, - "y": 0.051948051948052, - }, - Object { - "x": 1601389860000, - "y": 0, - }, - Object { - "x": 1601389890000, - "y": null, - }, - Object { - "x": 1601389920000, - "y": 0.325301204819277, - }, - Object { - "x": 1601389950000, - "y": 0.0201005025125628, - }, - Object { - "x": 1601389980000, - "y": 0.176470588235294, - }, - Object { - "x": 1601390010000, - "y": null, - }, - Object { - "x": 1601390040000, - "y": 0.244604316546763, - }, - Object { - "x": 1601390070000, - "y": 0.247619047619048, - }, - Object { - "x": 1601390100000, - "y": null, - }, - Object { - "x": 1601390130000, - "y": 0.266666666666667, - }, - Object { - "x": 1601390160000, - "y": 0.0961538461538462, - }, - Object { - "x": 1601390190000, - "y": 0, - }, - Object { - "x": 1601390220000, - "y": null, - }, - Object { - "x": 1601390250000, - "y": null, - }, - Object { - "x": 1601390280000, - "y": 0, - }, - Object { - "x": 1601390310000, - "y": null, - }, - Object { - "x": 1601390340000, - "y": null, - }, - Object { - "x": 1601390370000, - "y": 0, - }, - Object { - "x": 1601390400000, - "y": null, - }, - Object { - "x": 1601390430000, - "y": 0.25, - }, - Object { - "x": 1601390460000, - "y": 0.235294117647059, - }, - Object { - "x": 1601390490000, - "y": 0.0980392156862745, - }, - Object { - "x": 1601390520000, - "y": 0.235294117647059, - }, - Object { - "x": 1601390550000, - "y": 0.13953488372093, - }, - Object { - "x": 1601390580000, - "y": null, - }, - Object { - "x": 1601390610000, - "y": null, - }, - Object { - "x": 1601390640000, - "y": null, - }, - Object { - "x": 1601390670000, - "y": 0, - }, - Object { - "x": 1601390700000, - "y": null, - }, - Object { - "x": 1601390730000, - "y": null, - }, - Object { - "x": 1601390760000, - "y": 0.246376811594203, - }, - Object { - "x": 1601390790000, - "y": 0, - }, - Object { - "x": 1601390820000, - "y": null, - }, - Object { - "x": 1601390850000, - "y": 0.147058823529412, - }, - Object { - "x": 1601390880000, - "y": 0.222222222222222, - }, - Object { - "x": 1601390910000, - "y": null, - }, - Object { - "x": 1601390940000, - "y": null, - }, - Object { - "x": 1601390970000, - "y": 0.19047619047619, - }, - Object { - "x": 1601391000000, - "y": 0.126582278481013, - }, - Object { - "x": 1601391030000, - "y": null, - }, - Object { - "x": 1601391060000, - "y": null, - }, - Object { - "x": 1601391090000, - "y": null, - }, - Object { - "x": 1601391120000, - "y": 0.153284671532847, - }, - Object { - "x": 1601391150000, - "y": 0, - }, - Object { - "x": 1601391180000, - "y": null, - }, - Object { - "x": 1601391210000, - "y": 0.0740740740740741, - }, - Object { - "x": 1601391240000, - "y": 0, - }, - Object { - "x": 1601391270000, - "y": 0.114285714285714, - }, - Object { - "x": 1601391300000, - "y": null, - }, - Object { - "x": 1601391330000, - "y": null, - }, - Object { - "x": 1601391360000, - "y": 0, - }, - Object { - "x": 1601391390000, - "y": null, - }, - Object { - "x": 1601391420000, - "y": 0.235294117647059, - }, - Object { - "x": 1601391450000, - "y": null, - }, - Object { - "x": 1601391480000, - "y": 0, - }, - Object { - "x": 1601391510000, - "y": 0.0300751879699248, - }, - Object { - "x": 1601391540000, - "y": null, - }, - Object { - "x": 1601391570000, - "y": null, - }, - Object { - "x": 1601391600000, - "y": null, - }, - ], - "hideLegend": false, - "legendValue": "10%", - "title": "postgresql", - "type": "areaStacked", - }, - ], -} -`; - -exports[`Breakdown when data is loaded returns the transaction breakdown for a transaction group 9`] = ` -Array [ - Object { - "x": 1601389800000, - "y": 1, - }, - Object { - "x": 1601389830000, - "y": 1, - }, - Object { - "x": 1601389860000, - "y": 1, - }, - Object { - "x": 1601389890000, - "y": null, - }, - Object { - "x": 1601389920000, - "y": 1, - }, - Object { - "x": 1601389950000, - "y": 1, - }, - Object { - "x": 1601389980000, - "y": null, - }, - Object { - "x": 1601390010000, - "y": null, - }, - Object { - "x": 1601390040000, - "y": 1, - }, - Object { - "x": 1601390070000, - "y": 1, - }, - Object { - "x": 1601390100000, - "y": null, - }, - Object { - "x": 1601390130000, - "y": null, - }, - Object { - "x": 1601390160000, - "y": 1, - }, - Object { - "x": 1601390190000, - "y": 1, - }, - Object { - "x": 1601390220000, - "y": null, - }, - Object { - "x": 1601390250000, - "y": null, - }, - Object { - "x": 1601390280000, - "y": 1, - }, - Object { - "x": 1601390310000, - "y": null, - }, - Object { - "x": 1601390340000, - "y": null, - }, - Object { - "x": 1601390370000, - "y": null, - }, - Object { - "x": 1601390400000, - "y": null, - }, - Object { - "x": 1601390430000, - "y": null, - }, - Object { - "x": 1601390460000, - "y": null, - }, - Object { - "x": 1601390490000, - "y": 1, - }, - Object { - "x": 1601390520000, - "y": 1, - }, - Object { - "x": 1601390550000, - "y": 1, - }, - Object { - "x": 1601390580000, - "y": null, - }, - Object { - "x": 1601390610000, - "y": null, - }, - Object { - "x": 1601390640000, - "y": null, - }, - Object { - "x": 1601390670000, - "y": 1, - }, - Object { - "x": 1601390700000, - "y": null, - }, - Object { - "x": 1601390730000, - "y": null, - }, - Object { - "x": 1601390760000, - "y": 1, - }, - Object { - "x": 1601390790000, - "y": 1, - }, - Object { - "x": 1601390820000, - "y": null, - }, - Object { - "x": 1601390850000, - "y": 1, - }, - Object { - "x": 1601390880000, - "y": 1, - }, - Object { - "x": 1601390910000, - "y": null, - }, - Object { - "x": 1601390940000, - "y": null, - }, - Object { - "x": 1601390970000, - "y": 1, - }, - Object { - "x": 1601391000000, - "y": 1, - }, - Object { - "x": 1601391030000, - "y": null, - }, - Object { - "x": 1601391060000, - "y": null, - }, - Object { - "x": 1601391090000, - "y": null, - }, - Object { - "x": 1601391120000, - "y": 1, - }, - Object { - "x": 1601391150000, - "y": 1, - }, - Object { - "x": 1601391180000, - "y": null, - }, - Object { - "x": 1601391210000, - "y": 1, - }, - Object { - "x": 1601391240000, - "y": null, - }, - Object { - "x": 1601391270000, - "y": null, - }, - Object { - "x": 1601391300000, - "y": null, - }, - Object { - "x": 1601391330000, - "y": null, - }, - Object { - "x": 1601391360000, - "y": 1, - }, - Object { - "x": 1601391390000, - "y": null, - }, - Object { - "x": 1601391420000, - "y": null, - }, - Object { - "x": 1601391450000, - "y": null, - }, - Object { - "x": 1601391480000, - "y": 1, - }, - Object { - "x": 1601391510000, - "y": 1, - }, - Object { - "x": 1601391540000, - "y": null, - }, - Object { - "x": 1601391570000, - "y": null, - }, - Object { - "x": 1601391600000, - "y": null, - }, -] -`; diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/error_rate.snap b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/error_rate.snap deleted file mode 100644 index 1161beb7f06c0..0000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/error_rate.snap +++ /dev/null @@ -1,250 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Error rate when data is loaded returns the transaction error rate has the correct error rate 1`] = ` -Array [ - Object { - "x": 1601389800000, - "y": 0.166666666666667, - }, - Object { - "x": 1601389830000, - "y": null, - }, - Object { - "x": 1601389860000, - "y": null, - }, - Object { - "x": 1601389890000, - "y": null, - }, - Object { - "x": 1601389920000, - "y": 0, - }, - Object { - "x": 1601389950000, - "y": 0, - }, - Object { - "x": 1601389980000, - "y": null, - }, - Object { - "x": 1601390010000, - "y": null, - }, - Object { - "x": 1601390040000, - "y": 0, - }, - Object { - "x": 1601390070000, - "y": 0.5, - }, - Object { - "x": 1601390100000, - "y": null, - }, - Object { - "x": 1601390130000, - "y": null, - }, - Object { - "x": 1601390160000, - "y": 0.285714285714286, - }, - Object { - "x": 1601390190000, - "y": 0, - }, - Object { - "x": 1601390220000, - "y": 0, - }, - Object { - "x": 1601390250000, - "y": null, - }, - Object { - "x": 1601390280000, - "y": null, - }, - Object { - "x": 1601390310000, - "y": 0, - }, - Object { - "x": 1601390340000, - "y": null, - }, - Object { - "x": 1601390370000, - "y": null, - }, - Object { - "x": 1601390400000, - "y": 0, - }, - Object { - "x": 1601390430000, - "y": null, - }, - Object { - "x": 1601390460000, - "y": null, - }, - Object { - "x": 1601390490000, - "y": null, - }, - Object { - "x": 1601390520000, - "y": 0, - }, - Object { - "x": 1601390550000, - "y": 1, - }, - Object { - "x": 1601390580000, - "y": 0, - }, - Object { - "x": 1601390610000, - "y": null, - }, - Object { - "x": 1601390640000, - "y": 1, - }, - Object { - "x": 1601390670000, - "y": 0.5, - }, - Object { - "x": 1601390700000, - "y": null, - }, - Object { - "x": 1601390730000, - "y": null, - }, - Object { - "x": 1601390760000, - "y": 0.25, - }, - Object { - "x": 1601390790000, - "y": 0, - }, - Object { - "x": 1601390820000, - "y": 0, - }, - Object { - "x": 1601390850000, - "y": null, - }, - Object { - "x": 1601390880000, - "y": 0.166666666666667, - }, - Object { - "x": 1601390910000, - "y": null, - }, - Object { - "x": 1601390940000, - "y": 0.333333333333333, - }, - Object { - "x": 1601390970000, - "y": null, - }, - Object { - "x": 1601391000000, - "y": 0, - }, - Object { - "x": 1601391030000, - "y": null, - }, - Object { - "x": 1601391060000, - "y": 1, - }, - Object { - "x": 1601391090000, - "y": null, - }, - Object { - "x": 1601391120000, - "y": 0, - }, - Object { - "x": 1601391150000, - "y": 0, - }, - Object { - "x": 1601391180000, - "y": 0, - }, - Object { - "x": 1601391210000, - "y": null, - }, - Object { - "x": 1601391240000, - "y": 0, - }, - Object { - "x": 1601391270000, - "y": null, - }, - Object { - "x": 1601391300000, - "y": 0, - }, - Object { - "x": 1601391330000, - "y": null, - }, - Object { - "x": 1601391360000, - "y": 0, - }, - Object { - "x": 1601391390000, - "y": null, - }, - Object { - "x": 1601391420000, - "y": null, - }, - Object { - "x": 1601391450000, - "y": null, - }, - Object { - "x": 1601391480000, - "y": 0, - }, - Object { - "x": 1601391510000, - "y": 0, - }, - Object { - "x": 1601391540000, - "y": 1, - }, - Object { - "x": 1601391570000, - "y": null, - }, - Object { - "x": 1601391600000, - "y": null, - }, -] -`; diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/top_transaction_groups.snap b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/top_transaction_groups.snap deleted file mode 100644 index 9ff2294cdb08f..0000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/top_transaction_groups.snap +++ /dev/null @@ -1,126 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Top transaction groups when data is loaded returns the correct buckets (when ignoring samples) 1`] = ` -Array [ - Object { - "averageResponseTime": 2292, - "impact": 0, - "key": "GET /*", - "p95": 2288, - "serviceName": "opbeans-node", - "transactionName": "GET /*", - "transactionType": "request", - "transactionsPerMinute": 0.0333333333333333, - }, - Object { - "averageResponseTime": 10317, - "impact": 0.420340829629707, - "key": "GET /api/orders/:id", - "p95": 10304, - "serviceName": "opbeans-node", - "transactionName": "GET /api/orders/:id", - "transactionType": "request", - "transactionsPerMinute": 0.0333333333333333, - }, - Object { - "averageResponseTime": 6495, - "impact": 0.560349681667116, - "key": "GET /api/products/:id", - "p95": 6720, - "serviceName": "opbeans-node", - "transactionName": "GET /api/products/:id", - "transactionType": "request", - "transactionsPerMinute": 0.0666666666666667, - }, - Object { - "averageResponseTime": 9825.5, - "impact": 0.909245664989668, - "key": "GET /api/types", - "p95": 16496, - "serviceName": "opbeans-node", - "transactionName": "GET /api/types", - "transactionType": "request", - "transactionsPerMinute": 0.0666666666666667, - }, - Object { - "averageResponseTime": 9516.83333333333, - "impact": 2.87083620326164, - "key": "GET /api/products", - "p95": 17888, - "serviceName": "opbeans-node", - "transactionName": "GET /api/products", - "transactionType": "request", - "transactionsPerMinute": 0.2, - }, - Object { - "averageResponseTime": 13962.2, - "impact": 3.53657227112376, - "key": "GET /api/products/:id/customers", - "p95": 23264, - "serviceName": "opbeans-node", - "transactionName": "GET /api/products/:id/customers", - "transactionType": "request", - "transactionsPerMinute": 0.166666666666667, - }, - Object { - "averageResponseTime": 21129.5, - "impact": 4.3069090413872, - "key": "GET /api/customers/:id", - "p95": 32608, - "serviceName": "opbeans-node", - "transactionName": "GET /api/customers/:id", - "transactionType": "request", - "transactionsPerMinute": 0.133333333333333, - }, - Object { - "averageResponseTime": 10137.1111111111, - "impact": 4.65868586528666, - "key": "GET /api/orders", - "p95": 21344, - "serviceName": "opbeans-node", - "transactionName": "GET /api/orders", - "transactionType": "request", - "transactionsPerMinute": 0.3, - }, - Object { - "averageResponseTime": 24206.25, - "impact": 4.95153640465858, - "key": "GET /api/customers", - "p95": 36032, - "serviceName": "opbeans-node", - "transactionName": "GET /api/customers", - "transactionType": "request", - "transactionsPerMinute": 0.133333333333333, - }, - Object { - "averageResponseTime": 17267.0833333333, - "impact": 10.7331215479018, - "key": "GET /api/products/top", - "p95": 26208, - "serviceName": "opbeans-node", - "transactionName": "GET /api/products/top", - "transactionType": "request", - "transactionsPerMinute": 0.4, - }, - Object { - "averageResponseTime": 20417.7272727273, - "impact": 11.6439909593985, - "key": "GET /api/stats", - "p95": 24800, - "serviceName": "opbeans-node", - "transactionName": "GET /api/stats", - "transactionType": "request", - "transactionsPerMinute": 0.366666666666667, - }, - Object { - "averageResponseTime": 39822.0208333333, - "impact": 100, - "key": "GET /api", - "p95": 122816, - "serviceName": "opbeans-node", - "transactionName": "GET /api", - "transactionType": "request", - "transactionsPerMinute": 1.6, - }, -] -`; diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/transaction_charts.snap b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/transaction_charts.snap deleted file mode 100644 index a75b8918ed5e4..0000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/transaction_charts.snap +++ /dev/null @@ -1,1501 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Transaction charts when data is loaded returns the correct data 4`] = ` -Object { - "apmTimeseries": Object { - "overallAvgDuration": 600888.274678112, - "responseTimes": Object { - "avg": Array [ - Object { - "x": 1601389800000, - "y": 651784.714285714, - }, - Object { - "x": 1601389830000, - "y": 747797.4, - }, - Object { - "x": 1601389860000, - "y": 567568.333333333, - }, - Object { - "x": 1601389890000, - "y": 1289936, - }, - Object { - "x": 1601389920000, - "y": 79698.6, - }, - Object { - "x": 1601389950000, - "y": 646660.833333333, - }, - Object { - "x": 1601389980000, - "y": 18095, - }, - Object { - "x": 1601390010000, - "y": 543534, - }, - Object { - "x": 1601390040000, - "y": 250234.466666667, - }, - Object { - "x": 1601390070000, - "y": 200435.2, - }, - Object { - "x": 1601390100000, - "y": 1089389.66666667, - }, - Object { - "x": 1601390130000, - "y": 1052697.33333333, - }, - Object { - "x": 1601390160000, - "y": 27908.8333333333, - }, - Object { - "x": 1601390190000, - "y": 1078058.25, - }, - Object { - "x": 1601390220000, - "y": 755843.5, - }, - Object { - "x": 1601390250000, - "y": 1371940.33333333, - }, - Object { - "x": 1601390280000, - "y": 38056, - }, - Object { - "x": 1601390310000, - "y": 1133161.33333333, - }, - Object { - "x": 1601390340000, - "y": 1236497, - }, - Object { - "x": 1601390370000, - "y": 870027, - }, - Object { - "x": 1601390400000, - "y": null, - }, - Object { - "x": 1601390430000, - "y": 800475, - }, - Object { - "x": 1601390460000, - "y": 374597.2, - }, - Object { - "x": 1601390490000, - "y": 657002, - }, - Object { - "x": 1601390520000, - "y": 305164.5, - }, - Object { - "x": 1601390550000, - "y": 274576.4, - }, - Object { - "x": 1601390580000, - "y": 888533, - }, - Object { - "x": 1601390610000, - "y": 1191308, - }, - Object { - "x": 1601390640000, - "y": 1521297, - }, - Object { - "x": 1601390670000, - "y": 373994.4, - }, - Object { - "x": 1601390700000, - "y": 1108442, - }, - Object { - "x": 1601390730000, - "y": 1014666.66666667, - }, - Object { - "x": 1601390760000, - "y": 184717, - }, - Object { - "x": 1601390790000, - "y": 369595.5, - }, - Object { - "x": 1601390820000, - "y": 525805.5, - }, - Object { - "x": 1601390850000, - "y": 583359, - }, - Object { - "x": 1601390880000, - "y": 315244.25, - }, - Object { - "x": 1601390910000, - "y": 1133846, - }, - Object { - "x": 1601390940000, - "y": 312801, - }, - Object { - "x": 1601390970000, - "y": 1135768.33333333, - }, - Object { - "x": 1601391000000, - "y": 199876, - }, - Object { - "x": 1601391030000, - "y": 1508216.66666667, - }, - Object { - "x": 1601391060000, - "y": 1481690.5, - }, - Object { - "x": 1601391090000, - "y": 659469, - }, - Object { - "x": 1601391120000, - "y": 225622.666666667, - }, - Object { - "x": 1601391150000, - "y": 675812.666666667, - }, - Object { - "x": 1601391180000, - "y": 279013.333333333, - }, - Object { - "x": 1601391210000, - "y": 1327234, - }, - Object { - "x": 1601391240000, - "y": 487259, - }, - Object { - "x": 1601391270000, - "y": 686597.333333333, - }, - Object { - "x": 1601391300000, - "y": 1236063.33333333, - }, - Object { - "x": 1601391330000, - "y": 1322639, - }, - Object { - "x": 1601391360000, - "y": 517955.333333333, - }, - Object { - "x": 1601391390000, - "y": 983213.333333333, - }, - Object { - "x": 1601391420000, - "y": 920165.5, - }, - Object { - "x": 1601391450000, - "y": 655826, - }, - Object { - "x": 1601391480000, - "y": 335100.666666667, - }, - Object { - "x": 1601391510000, - "y": 496048.555555556, - }, - Object { - "x": 1601391540000, - "y": 629243, - }, - Object { - "x": 1601391570000, - "y": 796819.4, - }, - Object { - "x": 1601391600000, - "y": null, - }, - ], - "p95": Array [ - Object { - "x": 1601389800000, - "y": 1531888, - }, - Object { - "x": 1601389830000, - "y": 1695616, - }, - Object { - "x": 1601389860000, - "y": 1482496, - }, - Object { - "x": 1601389890000, - "y": 1617920, - }, - Object { - "x": 1601389920000, - "y": 329696, - }, - Object { - "x": 1601389950000, - "y": 1474432, - }, - Object { - "x": 1601389980000, - "y": 18048, - }, - Object { - "x": 1601390010000, - "y": 990720, - }, - Object { - "x": 1601390040000, - "y": 1163232, - }, - Object { - "x": 1601390070000, - "y": 958432, - }, - Object { - "x": 1601390100000, - "y": 1777600, - }, - Object { - "x": 1601390130000, - "y": 1873920, - }, - Object { - "x": 1601390160000, - "y": 55776, - }, - Object { - "x": 1601390190000, - "y": 1752064, - }, - Object { - "x": 1601390220000, - "y": 1136640, - }, - Object { - "x": 1601390250000, - "y": 1523712, - }, - Object { - "x": 1601390280000, - "y": 37888, - }, - Object { - "x": 1601390310000, - "y": 1196032, - }, - Object { - "x": 1601390340000, - "y": 1810304, - }, - Object { - "x": 1601390370000, - "y": 1007616, - }, - Object { - "x": 1601390400000, - "y": null, - }, - Object { - "x": 1601390430000, - "y": 1523584, - }, - Object { - "x": 1601390460000, - "y": 1712096, - }, - Object { - "x": 1601390490000, - "y": 679936, - }, - Object { - "x": 1601390520000, - "y": 1163200, - }, - Object { - "x": 1601390550000, - "y": 1171392, - }, - Object { - "x": 1601390580000, - "y": 901120, - }, - Object { - "x": 1601390610000, - "y": 1355776, - }, - Object { - "x": 1601390640000, - "y": 1515520, - }, - Object { - "x": 1601390670000, - "y": 1097600, - }, - Object { - "x": 1601390700000, - "y": 1363968, - }, - Object { - "x": 1601390730000, - "y": 1290240, - }, - Object { - "x": 1601390760000, - "y": 663488, - }, - Object { - "x": 1601390790000, - "y": 827264, - }, - Object { - "x": 1601390820000, - "y": 1302400, - }, - Object { - "x": 1601390850000, - "y": 978912, - }, - Object { - "x": 1601390880000, - "y": 1482720, - }, - Object { - "x": 1601390910000, - "y": 1306624, - }, - Object { - "x": 1601390940000, - "y": 1179520, - }, - Object { - "x": 1601390970000, - "y": 1347584, - }, - Object { - "x": 1601391000000, - "y": 1122272, - }, - Object { - "x": 1601391030000, - "y": 1835008, - }, - Object { - "x": 1601391060000, - "y": 1572864, - }, - Object { - "x": 1601391090000, - "y": 1343232, - }, - Object { - "x": 1601391120000, - "y": 810880, - }, - Object { - "x": 1601391150000, - "y": 1122048, - }, - Object { - "x": 1601391180000, - "y": 782208, - }, - Object { - "x": 1601391210000, - "y": 1466368, - }, - Object { - "x": 1601391240000, - "y": 1490928, - }, - Object { - "x": 1601391270000, - "y": 1433472, - }, - Object { - "x": 1601391300000, - "y": 1677312, - }, - Object { - "x": 1601391330000, - "y": 1830912, - }, - Object { - "x": 1601391360000, - "y": 950144, - }, - Object { - "x": 1601391390000, - "y": 1265664, - }, - Object { - "x": 1601391420000, - "y": 1408896, - }, - Object { - "x": 1601391450000, - "y": 1178624, - }, - Object { - "x": 1601391480000, - "y": 946048, - }, - Object { - "x": 1601391510000, - "y": 1761248, - }, - Object { - "x": 1601391540000, - "y": 626688, - }, - Object { - "x": 1601391570000, - "y": 1564544, - }, - Object { - "x": 1601391600000, - "y": null, - }, - ], - "p99": Array [ - Object { - "x": 1601389800000, - "y": 1531888, - }, - Object { - "x": 1601389830000, - "y": 1695616, - }, - Object { - "x": 1601389860000, - "y": 1482496, - }, - Object { - "x": 1601389890000, - "y": 1617920, - }, - Object { - "x": 1601389920000, - "y": 329696, - }, - Object { - "x": 1601389950000, - "y": 1474432, - }, - Object { - "x": 1601389980000, - "y": 18048, - }, - Object { - "x": 1601390010000, - "y": 990720, - }, - Object { - "x": 1601390040000, - "y": 1318880, - }, - Object { - "x": 1601390070000, - "y": 958432, - }, - Object { - "x": 1601390100000, - "y": 1777600, - }, - Object { - "x": 1601390130000, - "y": 1873920, - }, - Object { - "x": 1601390160000, - "y": 72160, - }, - Object { - "x": 1601390190000, - "y": 1752064, - }, - Object { - "x": 1601390220000, - "y": 1136640, - }, - Object { - "x": 1601390250000, - "y": 1523712, - }, - Object { - "x": 1601390280000, - "y": 37888, - }, - Object { - "x": 1601390310000, - "y": 1196032, - }, - Object { - "x": 1601390340000, - "y": 1810304, - }, - Object { - "x": 1601390370000, - "y": 1007616, - }, - Object { - "x": 1601390400000, - "y": null, - }, - Object { - "x": 1601390430000, - "y": 1523584, - }, - Object { - "x": 1601390460000, - "y": 1712096, - }, - Object { - "x": 1601390490000, - "y": 679936, - }, - Object { - "x": 1601390520000, - "y": 1163200, - }, - Object { - "x": 1601390550000, - "y": 1171392, - }, - Object { - "x": 1601390580000, - "y": 901120, - }, - Object { - "x": 1601390610000, - "y": 1355776, - }, - Object { - "x": 1601390640000, - "y": 1515520, - }, - Object { - "x": 1601390670000, - "y": 1097600, - }, - Object { - "x": 1601390700000, - "y": 1363968, - }, - Object { - "x": 1601390730000, - "y": 1290240, - }, - Object { - "x": 1601390760000, - "y": 663488, - }, - Object { - "x": 1601390790000, - "y": 827264, - }, - Object { - "x": 1601390820000, - "y": 1302400, - }, - Object { - "x": 1601390850000, - "y": 978912, - }, - Object { - "x": 1601390880000, - "y": 1482720, - }, - Object { - "x": 1601390910000, - "y": 1306624, - }, - Object { - "x": 1601390940000, - "y": 1179520, - }, - Object { - "x": 1601390970000, - "y": 1347584, - }, - Object { - "x": 1601391000000, - "y": 1122272, - }, - Object { - "x": 1601391030000, - "y": 1835008, - }, - Object { - "x": 1601391060000, - "y": 1572864, - }, - Object { - "x": 1601391090000, - "y": 1343232, - }, - Object { - "x": 1601391120000, - "y": 810880, - }, - Object { - "x": 1601391150000, - "y": 1122048, - }, - Object { - "x": 1601391180000, - "y": 782208, - }, - Object { - "x": 1601391210000, - "y": 1466368, - }, - Object { - "x": 1601391240000, - "y": 1490928, - }, - Object { - "x": 1601391270000, - "y": 1433472, - }, - Object { - "x": 1601391300000, - "y": 1677312, - }, - Object { - "x": 1601391330000, - "y": 1830912, - }, - Object { - "x": 1601391360000, - "y": 950144, - }, - Object { - "x": 1601391390000, - "y": 1265664, - }, - Object { - "x": 1601391420000, - "y": 1408896, - }, - Object { - "x": 1601391450000, - "y": 1178624, - }, - Object { - "x": 1601391480000, - "y": 946048, - }, - Object { - "x": 1601391510000, - "y": 1761248, - }, - Object { - "x": 1601391540000, - "y": 626688, - }, - Object { - "x": 1601391570000, - "y": 1564544, - }, - Object { - "x": 1601391600000, - "y": null, - }, - ], - }, - "tpmBuckets": Array [ - Object { - "avg": 3.3, - "dataPoints": Array [ - Object { - "x": 1601389800000, - "y": 6, - }, - Object { - "x": 1601389830000, - "y": 4, - }, - Object { - "x": 1601389860000, - "y": 2, - }, - Object { - "x": 1601389890000, - "y": 0, - }, - Object { - "x": 1601389920000, - "y": 8, - }, - Object { - "x": 1601389950000, - "y": 2, - }, - Object { - "x": 1601389980000, - "y": 2, - }, - Object { - "x": 1601390010000, - "y": 0, - }, - Object { - "x": 1601390040000, - "y": 22, - }, - Object { - "x": 1601390070000, - "y": 8, - }, - Object { - "x": 1601390100000, - "y": 2, - }, - Object { - "x": 1601390130000, - "y": 0, - }, - Object { - "x": 1601390160000, - "y": 20, - }, - Object { - "x": 1601390190000, - "y": 2, - }, - Object { - "x": 1601390220000, - "y": 0, - }, - Object { - "x": 1601390250000, - "y": 0, - }, - Object { - "x": 1601390280000, - "y": 2, - }, - Object { - "x": 1601390310000, - "y": 0, - }, - Object { - "x": 1601390340000, - "y": 2, - }, - Object { - "x": 1601390370000, - "y": 0, - }, - Object { - "x": 1601390400000, - "y": 0, - }, - Object { - "x": 1601390430000, - "y": 2, - }, - Object { - "x": 1601390460000, - "y": 8, - }, - Object { - "x": 1601390490000, - "y": 0, - }, - Object { - "x": 1601390520000, - "y": 6, - }, - Object { - "x": 1601390550000, - "y": 6, - }, - Object { - "x": 1601390580000, - "y": 0, - }, - Object { - "x": 1601390610000, - "y": 0, - }, - Object { - "x": 1601390640000, - "y": 0, - }, - Object { - "x": 1601390670000, - "y": 4, - }, - Object { - "x": 1601390700000, - "y": 0, - }, - Object { - "x": 1601390730000, - "y": 0, - }, - Object { - "x": 1601390760000, - "y": 4, - }, - Object { - "x": 1601390790000, - "y": 4, - }, - Object { - "x": 1601390820000, - "y": 6, - }, - Object { - "x": 1601390850000, - "y": 2, - }, - Object { - "x": 1601390880000, - "y": 12, - }, - Object { - "x": 1601390910000, - "y": 0, - }, - Object { - "x": 1601390940000, - "y": 6, - }, - Object { - "x": 1601390970000, - "y": 0, - }, - Object { - "x": 1601391000000, - "y": 10, - }, - Object { - "x": 1601391030000, - "y": 0, - }, - Object { - "x": 1601391060000, - "y": 0, - }, - Object { - "x": 1601391090000, - "y": 2, - }, - Object { - "x": 1601391120000, - "y": 8, - }, - Object { - "x": 1601391150000, - "y": 2, - }, - Object { - "x": 1601391180000, - "y": 4, - }, - Object { - "x": 1601391210000, - "y": 0, - }, - Object { - "x": 1601391240000, - "y": 6, - }, - Object { - "x": 1601391270000, - "y": 2, - }, - Object { - "x": 1601391300000, - "y": 0, - }, - Object { - "x": 1601391330000, - "y": 0, - }, - Object { - "x": 1601391360000, - "y": 2, - }, - Object { - "x": 1601391390000, - "y": 0, - }, - Object { - "x": 1601391420000, - "y": 2, - }, - Object { - "x": 1601391450000, - "y": 0, - }, - Object { - "x": 1601391480000, - "y": 4, - }, - Object { - "x": 1601391510000, - "y": 12, - }, - Object { - "x": 1601391540000, - "y": 0, - }, - Object { - "x": 1601391570000, - "y": 2, - }, - Object { - "x": 1601391600000, - "y": 0, - }, - ], - "key": "HTTP 2xx", - }, - Object { - "avg": 0.2, - "dataPoints": Array [ - Object { - "x": 1601389800000, - "y": 0, - }, - Object { - "x": 1601389830000, - "y": 0, - }, - Object { - "x": 1601389860000, - "y": 0, - }, - Object { - "x": 1601389890000, - "y": 0, - }, - Object { - "x": 1601389920000, - "y": 0, - }, - Object { - "x": 1601389950000, - "y": 2, - }, - Object { - "x": 1601389980000, - "y": 0, - }, - Object { - "x": 1601390010000, - "y": 0, - }, - Object { - "x": 1601390040000, - "y": 2, - }, - Object { - "x": 1601390070000, - "y": 0, - }, - Object { - "x": 1601390100000, - "y": 0, - }, - Object { - "x": 1601390130000, - "y": 0, - }, - Object { - "x": 1601390160000, - "y": 4, - }, - Object { - "x": 1601390190000, - "y": 0, - }, - Object { - "x": 1601390220000, - "y": 0, - }, - Object { - "x": 1601390250000, - "y": 0, - }, - Object { - "x": 1601390280000, - "y": 0, - }, - Object { - "x": 1601390310000, - "y": 0, - }, - Object { - "x": 1601390340000, - "y": 0, - }, - Object { - "x": 1601390370000, - "y": 0, - }, - Object { - "x": 1601390400000, - "y": 0, - }, - Object { - "x": 1601390430000, - "y": 0, - }, - Object { - "x": 1601390460000, - "y": 0, - }, - Object { - "x": 1601390490000, - "y": 0, - }, - Object { - "x": 1601390520000, - "y": 0, - }, - Object { - "x": 1601390550000, - "y": 0, - }, - Object { - "x": 1601390580000, - "y": 0, - }, - Object { - "x": 1601390610000, - "y": 0, - }, - Object { - "x": 1601390640000, - "y": 0, - }, - Object { - "x": 1601390670000, - "y": 2, - }, - Object { - "x": 1601390700000, - "y": 0, - }, - Object { - "x": 1601390730000, - "y": 0, - }, - Object { - "x": 1601390760000, - "y": 2, - }, - Object { - "x": 1601390790000, - "y": 0, - }, - Object { - "x": 1601390820000, - "y": 0, - }, - Object { - "x": 1601390850000, - "y": 0, - }, - Object { - "x": 1601390880000, - "y": 0, - }, - Object { - "x": 1601390910000, - "y": 0, - }, - Object { - "x": 1601390940000, - "y": 0, - }, - Object { - "x": 1601390970000, - "y": 0, - }, - Object { - "x": 1601391000000, - "y": 0, - }, - Object { - "x": 1601391030000, - "y": 0, - }, - Object { - "x": 1601391060000, - "y": 0, - }, - Object { - "x": 1601391090000, - "y": 0, - }, - Object { - "x": 1601391120000, - "y": 0, - }, - Object { - "x": 1601391150000, - "y": 0, - }, - Object { - "x": 1601391180000, - "y": 0, - }, - Object { - "x": 1601391210000, - "y": 0, - }, - Object { - "x": 1601391240000, - "y": 0, - }, - Object { - "x": 1601391270000, - "y": 0, - }, - Object { - "x": 1601391300000, - "y": 0, - }, - Object { - "x": 1601391330000, - "y": 0, - }, - Object { - "x": 1601391360000, - "y": 0, - }, - Object { - "x": 1601391390000, - "y": 0, - }, - Object { - "x": 1601391420000, - "y": 0, - }, - Object { - "x": 1601391450000, - "y": 0, - }, - Object { - "x": 1601391480000, - "y": 0, - }, - Object { - "x": 1601391510000, - "y": 0, - }, - Object { - "x": 1601391540000, - "y": 0, - }, - Object { - "x": 1601391570000, - "y": 0, - }, - Object { - "x": 1601391600000, - "y": 0, - }, - ], - "key": "HTTP 4xx", - }, - Object { - "avg": 4.26666666666667, - "dataPoints": Array [ - Object { - "x": 1601389800000, - "y": 8, - }, - Object { - "x": 1601389830000, - "y": 6, - }, - Object { - "x": 1601389860000, - "y": 4, - }, - Object { - "x": 1601389890000, - "y": 4, - }, - Object { - "x": 1601389920000, - "y": 2, - }, - Object { - "x": 1601389950000, - "y": 8, - }, - Object { - "x": 1601389980000, - "y": 0, - }, - Object { - "x": 1601390010000, - "y": 6, - }, - Object { - "x": 1601390040000, - "y": 6, - }, - Object { - "x": 1601390070000, - "y": 2, - }, - Object { - "x": 1601390100000, - "y": 4, - }, - Object { - "x": 1601390130000, - "y": 6, - }, - Object { - "x": 1601390160000, - "y": 0, - }, - Object { - "x": 1601390190000, - "y": 6, - }, - Object { - "x": 1601390220000, - "y": 4, - }, - Object { - "x": 1601390250000, - "y": 6, - }, - Object { - "x": 1601390280000, - "y": 0, - }, - Object { - "x": 1601390310000, - "y": 6, - }, - Object { - "x": 1601390340000, - "y": 6, - }, - Object { - "x": 1601390370000, - "y": 4, - }, - Object { - "x": 1601390400000, - "y": 0, - }, - Object { - "x": 1601390430000, - "y": 6, - }, - Object { - "x": 1601390460000, - "y": 2, - }, - Object { - "x": 1601390490000, - "y": 6, - }, - Object { - "x": 1601390520000, - "y": 2, - }, - Object { - "x": 1601390550000, - "y": 4, - }, - Object { - "x": 1601390580000, - "y": 4, - }, - Object { - "x": 1601390610000, - "y": 4, - }, - Object { - "x": 1601390640000, - "y": 2, - }, - Object { - "x": 1601390670000, - "y": 4, - }, - Object { - "x": 1601390700000, - "y": 4, - }, - Object { - "x": 1601390730000, - "y": 6, - }, - Object { - "x": 1601390760000, - "y": 2, - }, - Object { - "x": 1601390790000, - "y": 4, - }, - Object { - "x": 1601390820000, - "y": 6, - }, - Object { - "x": 1601390850000, - "y": 4, - }, - Object { - "x": 1601390880000, - "y": 4, - }, - Object { - "x": 1601390910000, - "y": 8, - }, - Object { - "x": 1601390940000, - "y": 2, - }, - Object { - "x": 1601390970000, - "y": 6, - }, - Object { - "x": 1601391000000, - "y": 2, - }, - Object { - "x": 1601391030000, - "y": 6, - }, - Object { - "x": 1601391060000, - "y": 4, - }, - Object { - "x": 1601391090000, - "y": 6, - }, - Object { - "x": 1601391120000, - "y": 4, - }, - Object { - "x": 1601391150000, - "y": 4, - }, - Object { - "x": 1601391180000, - "y": 2, - }, - Object { - "x": 1601391210000, - "y": 4, - }, - Object { - "x": 1601391240000, - "y": 4, - }, - Object { - "x": 1601391270000, - "y": 4, - }, - Object { - "x": 1601391300000, - "y": 6, - }, - Object { - "x": 1601391330000, - "y": 4, - }, - Object { - "x": 1601391360000, - "y": 4, - }, - Object { - "x": 1601391390000, - "y": 6, - }, - Object { - "x": 1601391420000, - "y": 6, - }, - Object { - "x": 1601391450000, - "y": 4, - }, - Object { - "x": 1601391480000, - "y": 2, - }, - Object { - "x": 1601391510000, - "y": 6, - }, - Object { - "x": 1601391540000, - "y": 2, - }, - Object { - "x": 1601391570000, - "y": 8, - }, - Object { - "x": 1601391600000, - "y": 0, - }, - ], - "key": "success", - }, - ], - }, -} -`; diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_load_dist.snap b/x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_load_dist.snap deleted file mode 100644 index 4bf242d8f9b6d..0000000000000 --- a/x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_load_dist.snap +++ /dev/null @@ -1,824 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`UX page load dist when there is data returns page load distribution 1`] = ` -Object { - "maxDuration": 54.46, - "minDuration": 0, - "pageLoadDistribution": Array [ - Object { - "x": 0, - "y": 0, - }, - Object { - "x": 0.5, - "y": 0, - }, - Object { - "x": 1, - "y": 0, - }, - Object { - "x": 1.5, - "y": 0, - }, - Object { - "x": 2, - "y": 0, - }, - Object { - "x": 2.5, - "y": 0, - }, - Object { - "x": 3, - "y": 16.6666666666667, - }, - Object { - "x": 3.5, - "y": 0, - }, - Object { - "x": 4, - "y": 0, - }, - Object { - "x": 4.5, - "y": 0, - }, - Object { - "x": 5, - "y": 50, - }, - Object { - "x": 5.5, - "y": 0, - }, - Object { - "x": 6, - "y": 0, - }, - Object { - "x": 6.5, - "y": 0, - }, - Object { - "x": 7, - "y": 0, - }, - Object { - "x": 7.5, - "y": 0, - }, - Object { - "x": 8, - "y": 0, - }, - Object { - "x": 8.5, - "y": 0, - }, - Object { - "x": 9, - "y": 0, - }, - Object { - "x": 9.5, - "y": 0, - }, - Object { - "x": 10, - "y": 0, - }, - Object { - "x": 10.5, - "y": 0, - }, - Object { - "x": 11, - "y": 0, - }, - Object { - "x": 11.5, - "y": 0, - }, - Object { - "x": 12, - "y": 0, - }, - Object { - "x": 12.5, - "y": 0, - }, - Object { - "x": 13, - "y": 0, - }, - Object { - "x": 13.5, - "y": 0, - }, - Object { - "x": 14, - "y": 0, - }, - Object { - "x": 14.5, - "y": 0, - }, - Object { - "x": 15, - "y": 0, - }, - Object { - "x": 15.5, - "y": 0, - }, - Object { - "x": 16, - "y": 0, - }, - Object { - "x": 16.5, - "y": 0, - }, - Object { - "x": 17, - "y": 0, - }, - Object { - "x": 17.5, - "y": 0, - }, - Object { - "x": 18, - "y": 0, - }, - Object { - "x": 18.5, - "y": 0, - }, - Object { - "x": 19, - "y": 0, - }, - Object { - "x": 19.5, - "y": 0, - }, - Object { - "x": 20, - "y": 0, - }, - Object { - "x": 20.5, - "y": 0, - }, - Object { - "x": 21, - "y": 0, - }, - Object { - "x": 21.5, - "y": 0, - }, - Object { - "x": 22, - "y": 0, - }, - Object { - "x": 22.5, - "y": 0, - }, - Object { - "x": 23, - "y": 0, - }, - Object { - "x": 23.5, - "y": 0, - }, - Object { - "x": 24, - "y": 0, - }, - Object { - "x": 24.5, - "y": 0, - }, - Object { - "x": 25, - "y": 0, - }, - Object { - "x": 25.5, - "y": 0, - }, - Object { - "x": 26, - "y": 0, - }, - Object { - "x": 26.5, - "y": 0, - }, - Object { - "x": 27, - "y": 0, - }, - Object { - "x": 27.5, - "y": 0, - }, - Object { - "x": 28, - "y": 0, - }, - Object { - "x": 28.5, - "y": 0, - }, - Object { - "x": 29, - "y": 0, - }, - Object { - "x": 29.5, - "y": 0, - }, - Object { - "x": 30, - "y": 0, - }, - Object { - "x": 30.5, - "y": 0, - }, - Object { - "x": 31, - "y": 0, - }, - Object { - "x": 31.5, - "y": 0, - }, - Object { - "x": 32, - "y": 0, - }, - Object { - "x": 32.5, - "y": 0, - }, - Object { - "x": 33, - "y": 0, - }, - Object { - "x": 33.5, - "y": 0, - }, - Object { - "x": 34, - "y": 0, - }, - Object { - "x": 34.5, - "y": 0, - }, - Object { - "x": 35, - "y": 0, - }, - Object { - "x": 35.5, - "y": 0, - }, - Object { - "x": 36, - "y": 0, - }, - Object { - "x": 36.5, - "y": 0, - }, - Object { - "x": 37, - "y": 0, - }, - Object { - "x": 37.5, - "y": 16.6666666666667, - }, - Object { - "x": 38, - "y": 0, - }, - Object { - "x": 38.5, - "y": 0, - }, - Object { - "x": 39, - "y": 0, - }, - Object { - "x": 39.5, - "y": 0, - }, - Object { - "x": 40, - "y": 0, - }, - Object { - "x": 40.5, - "y": 0, - }, - Object { - "x": 41, - "y": 0, - }, - Object { - "x": 41.5, - "y": 0, - }, - Object { - "x": 42, - "y": 0, - }, - Object { - "x": 42.5, - "y": 0, - }, - Object { - "x": 43, - "y": 0, - }, - Object { - "x": 43.5, - "y": 0, - }, - Object { - "x": 44, - "y": 0, - }, - Object { - "x": 44.5, - "y": 0, - }, - Object { - "x": 45, - "y": 0, - }, - Object { - "x": 45.5, - "y": 0, - }, - Object { - "x": 46, - "y": 0, - }, - Object { - "x": 46.5, - "y": 0, - }, - Object { - "x": 47, - "y": 0, - }, - Object { - "x": 47.5, - "y": 0, - }, - Object { - "x": 48, - "y": 0, - }, - Object { - "x": 48.5, - "y": 0, - }, - Object { - "x": 49, - "y": 0, - }, - Object { - "x": 49.5, - "y": 0, - }, - Object { - "x": 50, - "y": 0, - }, - Object { - "x": 50.5, - "y": 0, - }, - Object { - "x": 51, - "y": 0, - }, - Object { - "x": 51.5, - "y": 0, - }, - Object { - "x": 52, - "y": 0, - }, - Object { - "x": 52.5, - "y": 0, - }, - Object { - "x": 53, - "y": 0, - }, - Object { - "x": 53.5, - "y": 0, - }, - Object { - "x": 54, - "y": 0, - }, - Object { - "x": 54.5, - "y": 16.6666666666667, - }, - ], - "percentiles": Object { - "50.0": 4.88, - "75.0": 37.09, - "90.0": 37.09, - "95.0": 54.46, - "99.0": 54.46, - }, -} -`; - -exports[`UX page load dist when there is data returns page load distribution with breakdown 1`] = ` -Array [ - Object { - "data": Array [ - Object { - "x": 0, - "y": 0, - }, - Object { - "x": 0.5, - "y": 0, - }, - Object { - "x": 1, - "y": 0, - }, - Object { - "x": 1.5, - "y": 0, - }, - Object { - "x": 2, - "y": 0, - }, - Object { - "x": 2.5, - "y": 0, - }, - Object { - "x": 3, - "y": 25, - }, - Object { - "x": 3.5, - "y": 0, - }, - Object { - "x": 4, - "y": 0, - }, - Object { - "x": 4.5, - "y": 0, - }, - Object { - "x": 5, - "y": 25, - }, - Object { - "x": 5.5, - "y": 0, - }, - Object { - "x": 6, - "y": 0, - }, - Object { - "x": 6.5, - "y": 0, - }, - Object { - "x": 7, - "y": 0, - }, - Object { - "x": 7.5, - "y": 0, - }, - Object { - "x": 8, - "y": 0, - }, - Object { - "x": 8.5, - "y": 0, - }, - Object { - "x": 9, - "y": 0, - }, - Object { - "x": 9.5, - "y": 0, - }, - Object { - "x": 10, - "y": 0, - }, - Object { - "x": 10.5, - "y": 0, - }, - Object { - "x": 11, - "y": 0, - }, - Object { - "x": 11.5, - "y": 0, - }, - Object { - "x": 12, - "y": 0, - }, - Object { - "x": 12.5, - "y": 0, - }, - Object { - "x": 13, - "y": 0, - }, - Object { - "x": 13.5, - "y": 0, - }, - Object { - "x": 14, - "y": 0, - }, - Object { - "x": 14.5, - "y": 0, - }, - Object { - "x": 15, - "y": 0, - }, - Object { - "x": 15.5, - "y": 0, - }, - Object { - "x": 16, - "y": 0, - }, - Object { - "x": 16.5, - "y": 0, - }, - Object { - "x": 17, - "y": 0, - }, - Object { - "x": 17.5, - "y": 0, - }, - Object { - "x": 18, - "y": 0, - }, - Object { - "x": 18.5, - "y": 0, - }, - Object { - "x": 19, - "y": 0, - }, - Object { - "x": 19.5, - "y": 0, - }, - Object { - "x": 20, - "y": 0, - }, - Object { - "x": 20.5, - "y": 0, - }, - Object { - "x": 21, - "y": 0, - }, - Object { - "x": 21.5, - "y": 0, - }, - Object { - "x": 22, - "y": 0, - }, - Object { - "x": 22.5, - "y": 0, - }, - Object { - "x": 23, - "y": 0, - }, - Object { - "x": 23.5, - "y": 0, - }, - Object { - "x": 24, - "y": 0, - }, - Object { - "x": 24.5, - "y": 0, - }, - Object { - "x": 25, - "y": 0, - }, - Object { - "x": 25.5, - "y": 0, - }, - Object { - "x": 26, - "y": 0, - }, - Object { - "x": 26.5, - "y": 0, - }, - Object { - "x": 27, - "y": 0, - }, - Object { - "x": 27.5, - "y": 0, - }, - Object { - "x": 28, - "y": 0, - }, - Object { - "x": 28.5, - "y": 0, - }, - Object { - "x": 29, - "y": 0, - }, - Object { - "x": 29.5, - "y": 0, - }, - Object { - "x": 30, - "y": 0, - }, - Object { - "x": 30.5, - "y": 0, - }, - Object { - "x": 31, - "y": 0, - }, - Object { - "x": 31.5, - "y": 0, - }, - Object { - "x": 32, - "y": 0, - }, - Object { - "x": 32.5, - "y": 0, - }, - Object { - "x": 33, - "y": 0, - }, - Object { - "x": 33.5, - "y": 0, - }, - Object { - "x": 34, - "y": 0, - }, - Object { - "x": 34.5, - "y": 0, - }, - Object { - "x": 35, - "y": 0, - }, - Object { - "x": 35.5, - "y": 0, - }, - Object { - "x": 36, - "y": 0, - }, - Object { - "x": 36.5, - "y": 0, - }, - Object { - "x": 37, - "y": 0, - }, - Object { - "x": 37.5, - "y": 25, - }, - ], - "name": "Chrome", - }, - Object { - "data": Array [ - Object { - "x": 0, - "y": 0, - }, - Object { - "x": 0.5, - "y": 0, - }, - Object { - "x": 1, - "y": 0, - }, - Object { - "x": 1.5, - "y": 0, - }, - Object { - "x": 2, - "y": 0, - }, - Object { - "x": 2.5, - "y": 0, - }, - Object { - "x": 3, - "y": 0, - }, - Object { - "x": 3.5, - "y": 0, - }, - Object { - "x": 4, - "y": 0, - }, - Object { - "x": 4.5, - "y": 0, - }, - Object { - "x": 5, - "y": 100, - }, - ], - "name": "Chrome Mobile", - }, -] -`; - -exports[`UX page load dist when there is no data returns empty list 1`] = `Object {}`; - -exports[`UX page load dist when there is no data returns empty list with breakdowns 1`] = `Object {}`; diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_views.snap b/x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_views.snap deleted file mode 100644 index 38b009fc73d34..0000000000000 --- a/x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_views.snap +++ /dev/null @@ -1,280 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`CSM page views when there is data returns page views 1`] = ` -Object { - "items": Array [ - Object { - "x": 1600149947000, - "y": 1, - }, - Object { - "x": 1600149957000, - "y": 0, - }, - Object { - "x": 1600149967000, - "y": 0, - }, - Object { - "x": 1600149977000, - "y": 0, - }, - Object { - "x": 1600149987000, - "y": 0, - }, - Object { - "x": 1600149997000, - "y": 0, - }, - Object { - "x": 1600150007000, - "y": 0, - }, - Object { - "x": 1600150017000, - "y": 0, - }, - Object { - "x": 1600150027000, - "y": 1, - }, - Object { - "x": 1600150037000, - "y": 0, - }, - Object { - "x": 1600150047000, - "y": 0, - }, - Object { - "x": 1600150057000, - "y": 0, - }, - Object { - "x": 1600150067000, - "y": 0, - }, - Object { - "x": 1600150077000, - "y": 1, - }, - Object { - "x": 1600150087000, - "y": 0, - }, - Object { - "x": 1600150097000, - "y": 0, - }, - Object { - "x": 1600150107000, - "y": 0, - }, - Object { - "x": 1600150117000, - "y": 0, - }, - Object { - "x": 1600150127000, - "y": 0, - }, - Object { - "x": 1600150137000, - "y": 0, - }, - Object { - "x": 1600150147000, - "y": 0, - }, - Object { - "x": 1600150157000, - "y": 0, - }, - Object { - "x": 1600150167000, - "y": 0, - }, - Object { - "x": 1600150177000, - "y": 1, - }, - Object { - "x": 1600150187000, - "y": 0, - }, - Object { - "x": 1600150197000, - "y": 0, - }, - Object { - "x": 1600150207000, - "y": 1, - }, - Object { - "x": 1600150217000, - "y": 0, - }, - Object { - "x": 1600150227000, - "y": 0, - }, - Object { - "x": 1600150237000, - "y": 1, - }, - ], - "topItems": Array [], -} -`; - -exports[`CSM page views when there is data returns page views with breakdown 1`] = ` -Object { - "items": Array [ - Object { - "Chrome": 1, - "x": 1600149947000, - "y": 1, - }, - Object { - "x": 1600149957000, - "y": 0, - }, - Object { - "x": 1600149967000, - "y": 0, - }, - Object { - "x": 1600149977000, - "y": 0, - }, - Object { - "x": 1600149987000, - "y": 0, - }, - Object { - "x": 1600149997000, - "y": 0, - }, - Object { - "x": 1600150007000, - "y": 0, - }, - Object { - "x": 1600150017000, - "y": 0, - }, - Object { - "Chrome": 1, - "x": 1600150027000, - "y": 1, - }, - Object { - "x": 1600150037000, - "y": 0, - }, - Object { - "x": 1600150047000, - "y": 0, - }, - Object { - "x": 1600150057000, - "y": 0, - }, - Object { - "x": 1600150067000, - "y": 0, - }, - Object { - "Chrome": 1, - "x": 1600150077000, - "y": 1, - }, - Object { - "x": 1600150087000, - "y": 0, - }, - Object { - "x": 1600150097000, - "y": 0, - }, - Object { - "x": 1600150107000, - "y": 0, - }, - Object { - "x": 1600150117000, - "y": 0, - }, - Object { - "x": 1600150127000, - "y": 0, - }, - Object { - "x": 1600150137000, - "y": 0, - }, - Object { - "x": 1600150147000, - "y": 0, - }, - Object { - "x": 1600150157000, - "y": 0, - }, - Object { - "x": 1600150167000, - "y": 0, - }, - Object { - "Chrome": 1, - "x": 1600150177000, - "y": 1, - }, - Object { - "x": 1600150187000, - "y": 0, - }, - Object { - "x": 1600150197000, - "y": 0, - }, - Object { - "Chrome Mobile": 1, - "x": 1600150207000, - "y": 1, - }, - Object { - "x": 1600150217000, - "y": 0, - }, - Object { - "x": 1600150227000, - "y": 0, - }, - Object { - "Chrome Mobile": 1, - "x": 1600150237000, - "y": 1, - }, - ], - "topItems": Array [ - "Chrome", - "Chrome Mobile", - ], -} -`; - -exports[`CSM page views when there is no data returns empty list 1`] = ` -Object { - "items": Array [], - "topItems": Array [], -} -`; - -exports[`CSM page views when there is no data returns empty list with breakdowns 1`] = ` -Object { - "items": Array [], - "topItems": Array [], -} -`; diff --git a/x-pack/test/apm_api_integration/trial/tests/service_maps/__snapshots__/service_maps.snap b/x-pack/test/apm_api_integration/trial/tests/service_maps/__snapshots__/service_maps.snap deleted file mode 100644 index a7e6ae03b1bdc..0000000000000 --- a/x-pack/test/apm_api_integration/trial/tests/service_maps/__snapshots__/service_maps.snap +++ /dev/null @@ -1,1995 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Service Maps with a trial license /api/apm/service-map when there is data returns service map elements filtering by environment not defined 1`] = ` -Object { - "elements": Array [ - Object { - "data": Object { - "agent.name": "rum-js", - "id": "elastic-co-frontend", - "service.environment": "ENVIRONMENT_NOT_DEFINED", - "service.name": "elastic-co-frontend", - "serviceAnomalyStats": Object { - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-environment_not_defined-7ed6-high_mean_transaction_duration", - "transactionType": "page-load", - }, - }, - }, - Object { - "data": Object { - "groupedConnections": Array [ - Object { - "id": ">a18132920325.cdn.optimizely.com:443", - "label": "a18132920325.cdn.optimizely.com:443", - "span.destination.service.resource": "a18132920325.cdn.optimizely.com:443", - "span.subtype": "iframe", - "span.type": "resource", - }, - Object { - "id": ">cdn.optimizely.com:443", - "label": "cdn.optimizely.com:443", - "span.destination.service.resource": "cdn.optimizely.com:443", - "span.subtype": "script", - "span.type": "resource", - }, - Object { - "id": ">fonts.googleapis.com:443", - "label": "fonts.googleapis.com:443", - "span.destination.service.resource": "fonts.googleapis.com:443", - "span.subtype": "link", - "span.type": "resource", - }, - Object { - "id": ">images.contentstack.io:443", - "label": "images.contentstack.io:443", - "span.destination.service.resource": "images.contentstack.io:443", - "span.subtype": "css", - "span.type": "resource", - }, - Object { - "id": ">info.elastic.co:443", - "label": "info.elastic.co:443", - "span.destination.service.resource": "info.elastic.co:443", - "span.subtype": "script", - "span.type": "resource", - }, - Object { - "id": ">p.typekit.net:443", - "label": "p.typekit.net:443", - "span.destination.service.resource": "p.typekit.net:443", - "span.subtype": "css", - "span.type": "resource", - }, - Object { - "id": ">static-www.elastic.co:443", - "label": "static-www.elastic.co:443", - "span.destination.service.resource": "static-www.elastic.co:443", - "span.subtype": "img", - "span.type": "resource", - }, - Object { - "id": ">use.typekit.net:443", - "label": "use.typekit.net:443", - "span.destination.service.resource": "use.typekit.net:443", - "span.subtype": "link", - "span.type": "resource", - }, - Object { - "id": ">www.elastic.co:443", - "label": "www.elastic.co:443", - "span.destination.service.resource": "www.elastic.co:443", - "span.subtype": "browser-timing", - "span.type": "hard-navigation", - }, - ], - "id": "resourceGroup{elastic-co-frontend}", - "label": "9 resources", - "span.type": "external", - }, - }, - Object { - "data": Object { - "id": "elastic-co-frontend~>resourceGroup{elastic-co-frontend}", - "source": "elastic-co-frontend", - "target": "resourceGroup{elastic-co-frontend}", - }, - }, - ], -} -`; - -exports[`Service Maps with a trial license /api/apm/service-map when there is data returns the correct data 3`] = ` -Array [ - Object { - "data": Object { - "agent.name": "ruby", - "id": "opbeans-ruby", - "service.environment": "production", - "service.name": "opbeans-ruby", - "serviceAnomalyStats": Object { - "actualValue": 141536.936507937, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - Object { - "data": Object { - "agent.name": "java", - "id": "opbeans-java", - "service.environment": "production", - "service.name": "opbeans-java", - "serviceAnomalyStats": Object { - "actualValue": 559010.6, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - Object { - "data": Object { - "id": ">postgresql", - "label": "postgresql", - "span.destination.service.resource": "postgresql", - "span.subtype": "postgresql", - "span.type": "db", - }, - }, - Object { - "data": Object { - "agent.name": "rum-js", - "id": "elastic-co-frontend", - "service.name": "elastic-co-frontend", - "serviceAnomalyStats": Object { - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-environment_not_defined-7ed6-high_mean_transaction_duration", - "transactionType": "page-load", - }, - }, - }, - Object { - "data": Object { - "agent.name": "rum-js", - "id": "opbeans-rum", - "service.environment": "testing", - "service.name": "opbeans-rum", - "serviceAnomalyStats": Object { - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-environment_not_defined-7ed6-high_mean_transaction_duration", - "transactionType": "page-load", - }, - }, - }, - Object { - "data": Object { - "agent.name": "nodejs", - "id": "opbeans-node", - "service.environment": "testing", - "service.name": "opbeans-node", - }, - }, - Object { - "data": Object { - "agent.name": "go", - "id": "opbeans-go", - "service.environment": "testing", - "service.name": "opbeans-go", - }, - }, - Object { - "data": Object { - "agent.name": "python", - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "serviceAnomalyStats": Object { - "actualValue": 47107.7692307692, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - Object { - "data": Object { - "id": ">elasticsearch", - "label": "elasticsearch", - "span.destination.service.resource": "elasticsearch", - "span.subtype": "elasticsearch", - "span.type": "db", - }, - }, - Object { - "data": Object { - "id": ">redis", - "label": "redis", - "span.destination.service.resource": "redis", - "span.subtype": "redis", - "span.type": "db", - }, - }, - Object { - "data": Object { - "agent.name": "dotnet", - "id": "opbeans-dotnet", - "service.environment": null, - "service.name": "opbeans-dotnet", - }, - }, - Object { - "data": Object { - "groupedConnections": Array [ - Object { - "id": ">a18132920325.cdn.optimizely.com:443", - "label": "a18132920325.cdn.optimizely.com:443", - "span.destination.service.resource": "a18132920325.cdn.optimizely.com:443", - "span.subtype": "iframe", - "span.type": "resource", - }, - Object { - "id": ">cdn.optimizely.com:443", - "label": "cdn.optimizely.com:443", - "span.destination.service.resource": "cdn.optimizely.com:443", - "span.subtype": "script", - "span.type": "resource", - }, - Object { - "id": ">fonts.googleapis.com:443", - "label": "fonts.googleapis.com:443", - "span.destination.service.resource": "fonts.googleapis.com:443", - "span.subtype": "link", - "span.type": "resource", - }, - Object { - "id": ">images.contentstack.io:443", - "label": "images.contentstack.io:443", - "span.destination.service.resource": "images.contentstack.io:443", - "span.subtype": "css", - "span.type": "resource", - }, - Object { - "id": ">info.elastic.co:443", - "label": "info.elastic.co:443", - "span.destination.service.resource": "info.elastic.co:443", - "span.subtype": "script", - "span.type": "resource", - }, - Object { - "id": ">p.typekit.net:443", - "label": "p.typekit.net:443", - "span.destination.service.resource": "p.typekit.net:443", - "span.subtype": "css", - "span.type": "resource", - }, - Object { - "id": ">static-www.elastic.co:443", - "label": "static-www.elastic.co:443", - "span.destination.service.resource": "static-www.elastic.co:443", - "span.subtype": "img", - "span.type": "resource", - }, - Object { - "id": ">use.typekit.net:443", - "label": "use.typekit.net:443", - "span.destination.service.resource": "use.typekit.net:443", - "span.subtype": "link", - "span.type": "resource", - }, - Object { - "id": ">www.elastic.co:443", - "label": "www.elastic.co:443", - "span.destination.service.resource": "www.elastic.co:443", - "span.subtype": "browser-timing", - "span.type": "hard-navigation", - }, - ], - "id": "resourceGroup{elastic-co-frontend}", - "label": "9 resources", - "span.type": "external", - }, - }, - Object { - "data": Object { - "id": "opbeans-go~>postgresql", - "source": "opbeans-go", - "sourceData": Object { - "agent.name": "go", - "id": "opbeans-go", - "service.environment": "testing", - "service.name": "opbeans-go", - }, - "target": ">postgresql", - "targetData": Object { - "id": ">postgresql", - "label": "postgresql", - "span.destination.service.resource": "postgresql", - "span.subtype": "postgresql", - "span.type": "db", - }, - }, - }, - Object { - "data": Object { - "bidirectional": true, - "id": "opbeans-go~opbeans-node", - "source": "opbeans-go", - "sourceData": Object { - "agent.name": "go", - "id": "opbeans-go", - "service.environment": "testing", - "service.name": "opbeans-go", - }, - "target": "opbeans-node", - "targetData": Object { - "agent.name": "nodejs", - "id": "opbeans-node", - "service.environment": "testing", - "service.name": "opbeans-node", - }, - }, - }, - Object { - "data": Object { - "bidirectional": true, - "id": "opbeans-go~opbeans-python", - "source": "opbeans-go", - "sourceData": Object { - "agent.name": "go", - "id": "opbeans-go", - "service.environment": "testing", - "service.name": "opbeans-go", - }, - "target": "opbeans-python", - "targetData": Object { - "agent.name": "python", - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "serviceAnomalyStats": Object { - "actualValue": 47107.7692307692, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-java~>postgresql", - "source": "opbeans-java", - "sourceData": Object { - "agent.name": "java", - "id": "opbeans-java", - "service.environment": "production", - "service.name": "opbeans-java", - "serviceAnomalyStats": Object { - "actualValue": 559010.6, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": ">postgresql", - "targetData": Object { - "id": ">postgresql", - "label": "postgresql", - "span.destination.service.resource": "postgresql", - "span.subtype": "postgresql", - "span.type": "db", - }, - }, - }, - Object { - "data": Object { - "bidirectional": true, - "id": "opbeans-java~opbeans-node", - "source": "opbeans-java", - "sourceData": Object { - "agent.name": "java", - "id": "opbeans-java", - "service.environment": "production", - "service.name": "opbeans-java", - "serviceAnomalyStats": Object { - "actualValue": 559010.6, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": "opbeans-node", - "targetData": Object { - "agent.name": "nodejs", - "id": "opbeans-node", - "service.environment": "testing", - "service.name": "opbeans-node", - }, - }, - }, - Object { - "data": Object { - "bidirectional": true, - "id": "opbeans-java~opbeans-ruby", - "source": "opbeans-java", - "sourceData": Object { - "agent.name": "java", - "id": "opbeans-java", - "service.environment": "production", - "service.name": "opbeans-java", - "serviceAnomalyStats": Object { - "actualValue": 559010.6, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": "opbeans-ruby", - "targetData": Object { - "agent.name": "ruby", - "id": "opbeans-ruby", - "service.environment": "production", - "service.name": "opbeans-ruby", - "serviceAnomalyStats": Object { - "actualValue": 141536.936507937, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-node~>postgresql", - "source": "opbeans-node", - "sourceData": Object { - "agent.name": "nodejs", - "id": "opbeans-node", - "service.environment": "testing", - "service.name": "opbeans-node", - }, - "target": ">postgresql", - "targetData": Object { - "id": ">postgresql", - "label": "postgresql", - "span.destination.service.resource": "postgresql", - "span.subtype": "postgresql", - "span.type": "db", - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-node~opbeans-go", - "isInverseEdge": true, - "source": "opbeans-node", - "sourceData": Object { - "agent.name": "nodejs", - "id": "opbeans-node", - "service.environment": "testing", - "service.name": "opbeans-node", - }, - "target": "opbeans-go", - "targetData": Object { - "agent.name": "go", - "id": "opbeans-go", - "service.environment": "testing", - "service.name": "opbeans-go", - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-node~opbeans-java", - "isInverseEdge": true, - "source": "opbeans-node", - "sourceData": Object { - "agent.name": "nodejs", - "id": "opbeans-node", - "service.environment": "testing", - "service.name": "opbeans-node", - }, - "target": "opbeans-java", - "targetData": Object { - "agent.name": "java", - "id": "opbeans-java", - "service.environment": "production", - "service.name": "opbeans-java", - "serviceAnomalyStats": Object { - "actualValue": 559010.6, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - }, - Object { - "data": Object { - "bidirectional": true, - "id": "opbeans-node~opbeans-python", - "source": "opbeans-node", - "sourceData": Object { - "agent.name": "nodejs", - "id": "opbeans-node", - "service.environment": "testing", - "service.name": "opbeans-node", - }, - "target": "opbeans-python", - "targetData": Object { - "agent.name": "python", - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "serviceAnomalyStats": Object { - "actualValue": 47107.7692307692, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - }, - Object { - "data": Object { - "bidirectional": true, - "id": "opbeans-node~opbeans-ruby", - "source": "opbeans-node", - "sourceData": Object { - "agent.name": "nodejs", - "id": "opbeans-node", - "service.environment": "testing", - "service.name": "opbeans-node", - }, - "target": "opbeans-ruby", - "targetData": Object { - "agent.name": "ruby", - "id": "opbeans-ruby", - "service.environment": "production", - "service.name": "opbeans-ruby", - "serviceAnomalyStats": Object { - "actualValue": 141536.936507937, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-python~>elasticsearch", - "source": "opbeans-python", - "sourceData": Object { - "agent.name": "python", - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "serviceAnomalyStats": Object { - "actualValue": 47107.7692307692, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": ">elasticsearch", - "targetData": Object { - "id": ">elasticsearch", - "label": "elasticsearch", - "span.destination.service.resource": "elasticsearch", - "span.subtype": "elasticsearch", - "span.type": "db", - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-python~>postgresql", - "source": "opbeans-python", - "sourceData": Object { - "agent.name": "python", - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "serviceAnomalyStats": Object { - "actualValue": 47107.7692307692, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": ">postgresql", - "targetData": Object { - "id": ">postgresql", - "label": "postgresql", - "span.destination.service.resource": "postgresql", - "span.subtype": "postgresql", - "span.type": "db", - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-python~>redis", - "source": "opbeans-python", - "sourceData": Object { - "agent.name": "python", - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "serviceAnomalyStats": Object { - "actualValue": 47107.7692307692, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": ">redis", - "targetData": Object { - "id": ">redis", - "label": "redis", - "span.destination.service.resource": "redis", - "span.subtype": "redis", - "span.type": "db", - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-python~opbeans-go", - "isInverseEdge": true, - "source": "opbeans-python", - "sourceData": Object { - "agent.name": "python", - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "serviceAnomalyStats": Object { - "actualValue": 47107.7692307692, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": "opbeans-go", - "targetData": Object { - "agent.name": "go", - "id": "opbeans-go", - "service.environment": "testing", - "service.name": "opbeans-go", - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-python~opbeans-node", - "isInverseEdge": true, - "source": "opbeans-python", - "sourceData": Object { - "agent.name": "python", - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "serviceAnomalyStats": Object { - "actualValue": 47107.7692307692, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": "opbeans-node", - "targetData": Object { - "agent.name": "nodejs", - "id": "opbeans-node", - "service.environment": "testing", - "service.name": "opbeans-node", - }, - }, - }, - Object { - "data": Object { - "bidirectional": true, - "id": "opbeans-python~opbeans-ruby", - "source": "opbeans-python", - "sourceData": Object { - "agent.name": "python", - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "serviceAnomalyStats": Object { - "actualValue": 47107.7692307692, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": "opbeans-ruby", - "targetData": Object { - "agent.name": "ruby", - "id": "opbeans-ruby", - "service.environment": "production", - "service.name": "opbeans-ruby", - "serviceAnomalyStats": Object { - "actualValue": 141536.936507937, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-ruby~>postgresql", - "source": "opbeans-ruby", - "sourceData": Object { - "agent.name": "ruby", - "id": "opbeans-ruby", - "service.environment": "production", - "service.name": "opbeans-ruby", - "serviceAnomalyStats": Object { - "actualValue": 141536.936507937, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": ">postgresql", - "targetData": Object { - "id": ">postgresql", - "label": "postgresql", - "span.destination.service.resource": "postgresql", - "span.subtype": "postgresql", - "span.type": "db", - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-ruby~opbeans-go", - "source": "opbeans-ruby", - "sourceData": Object { - "agent.name": "ruby", - "id": "opbeans-ruby", - "service.environment": "production", - "service.name": "opbeans-ruby", - "serviceAnomalyStats": Object { - "actualValue": 141536.936507937, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": "opbeans-go", - "targetData": Object { - "agent.name": "go", - "id": "opbeans-go", - "service.environment": "testing", - "service.name": "opbeans-go", - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-ruby~opbeans-java", - "isInverseEdge": true, - "source": "opbeans-ruby", - "sourceData": Object { - "agent.name": "ruby", - "id": "opbeans-ruby", - "service.environment": "production", - "service.name": "opbeans-ruby", - "serviceAnomalyStats": Object { - "actualValue": 141536.936507937, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": "opbeans-java", - "targetData": Object { - "agent.name": "java", - "id": "opbeans-java", - "service.environment": "production", - "service.name": "opbeans-java", - "serviceAnomalyStats": Object { - "actualValue": 559010.6, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-ruby~opbeans-node", - "isInverseEdge": true, - "source": "opbeans-ruby", - "sourceData": Object { - "agent.name": "ruby", - "id": "opbeans-ruby", - "service.environment": "production", - "service.name": "opbeans-ruby", - "serviceAnomalyStats": Object { - "actualValue": 141536.936507937, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": "opbeans-node", - "targetData": Object { - "agent.name": "nodejs", - "id": "opbeans-node", - "service.environment": "testing", - "service.name": "opbeans-node", - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-ruby~opbeans-python", - "isInverseEdge": true, - "source": "opbeans-ruby", - "sourceData": Object { - "agent.name": "ruby", - "id": "opbeans-ruby", - "service.environment": "production", - "service.name": "opbeans-ruby", - "serviceAnomalyStats": Object { - "actualValue": 141536.936507937, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": "opbeans-python", - "targetData": Object { - "agent.name": "python", - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "serviceAnomalyStats": Object { - "actualValue": 47107.7692307692, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-rum~opbeans-go", - "source": "opbeans-rum", - "sourceData": Object { - "agent.name": "rum-js", - "id": "opbeans-rum", - "service.environment": "testing", - "service.name": "opbeans-rum", - "serviceAnomalyStats": Object { - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-environment_not_defined-7ed6-high_mean_transaction_duration", - "transactionType": "page-load", - }, - }, - "target": "opbeans-go", - "targetData": Object { - "agent.name": "go", - "id": "opbeans-go", - "service.environment": "testing", - "service.name": "opbeans-go", - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-rum~opbeans-java", - "source": "opbeans-rum", - "sourceData": Object { - "agent.name": "rum-js", - "id": "opbeans-rum", - "service.environment": "testing", - "service.name": "opbeans-rum", - "serviceAnomalyStats": Object { - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-environment_not_defined-7ed6-high_mean_transaction_duration", - "transactionType": "page-load", - }, - }, - "target": "opbeans-java", - "targetData": Object { - "agent.name": "java", - "id": "opbeans-java", - "service.environment": "production", - "service.name": "opbeans-java", - "serviceAnomalyStats": Object { - "actualValue": 559010.6, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-rum~opbeans-node", - "source": "opbeans-rum", - "sourceData": Object { - "agent.name": "rum-js", - "id": "opbeans-rum", - "service.environment": "testing", - "service.name": "opbeans-rum", - "serviceAnomalyStats": Object { - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-environment_not_defined-7ed6-high_mean_transaction_duration", - "transactionType": "page-load", - }, - }, - "target": "opbeans-node", - "targetData": Object { - "agent.name": "nodejs", - "id": "opbeans-node", - "service.environment": "testing", - "service.name": "opbeans-node", - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-rum~opbeans-python", - "source": "opbeans-rum", - "sourceData": Object { - "agent.name": "rum-js", - "id": "opbeans-rum", - "service.environment": "testing", - "service.name": "opbeans-rum", - "serviceAnomalyStats": Object { - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-environment_not_defined-7ed6-high_mean_transaction_duration", - "transactionType": "page-load", - }, - }, - "target": "opbeans-python", - "targetData": Object { - "agent.name": "python", - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "serviceAnomalyStats": Object { - "actualValue": 47107.7692307692, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-rum~opbeans-ruby", - "source": "opbeans-rum", - "sourceData": Object { - "agent.name": "rum-js", - "id": "opbeans-rum", - "service.environment": "testing", - "service.name": "opbeans-rum", - "serviceAnomalyStats": Object { - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-environment_not_defined-7ed6-high_mean_transaction_duration", - "transactionType": "page-load", - }, - }, - "target": "opbeans-ruby", - "targetData": Object { - "agent.name": "ruby", - "id": "opbeans-ruby", - "service.environment": "production", - "service.name": "opbeans-ruby", - "serviceAnomalyStats": Object { - "actualValue": 141536.936507937, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - }, - Object { - "data": Object { - "id": "elastic-co-frontend~>resourceGroup{elastic-co-frontend}", - "source": "elastic-co-frontend", - "target": "resourceGroup{elastic-co-frontend}", - }, - }, -] -`; - -exports[`Service Maps with a trial license when there is data with anomalies with the default apm user returns the correct anomaly stats 3`] = ` -Object { - "elements": Array [ - Object { - "data": Object { - "agent.name": "ruby", - "id": "opbeans-ruby", - "service.environment": "production", - "service.name": "opbeans-ruby", - "serviceAnomalyStats": Object { - "actualValue": 141536.936507937, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - Object { - "data": Object { - "agent.name": "java", - "id": "opbeans-java", - "service.environment": "production", - "service.name": "opbeans-java", - "serviceAnomalyStats": Object { - "actualValue": 559010.6, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - Object { - "data": Object { - "id": ">postgresql", - "label": "postgresql", - "span.destination.service.resource": "postgresql", - "span.subtype": "postgresql", - "span.type": "db", - }, - }, - Object { - "data": Object { - "agent.name": "rum-js", - "id": "elastic-co-frontend", - "service.name": "elastic-co-frontend", - "serviceAnomalyStats": Object { - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-environment_not_defined-7ed6-high_mean_transaction_duration", - "transactionType": "page-load", - }, - }, - }, - Object { - "data": Object { - "agent.name": "rum-js", - "id": "opbeans-rum", - "service.environment": "testing", - "service.name": "opbeans-rum", - "serviceAnomalyStats": Object { - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-environment_not_defined-7ed6-high_mean_transaction_duration", - "transactionType": "page-load", - }, - }, - }, - Object { - "data": Object { - "agent.name": "nodejs", - "id": "opbeans-node", - "service.environment": "testing", - "service.name": "opbeans-node", - }, - }, - Object { - "data": Object { - "agent.name": "go", - "id": "opbeans-go", - "service.environment": "testing", - "service.name": "opbeans-go", - }, - }, - Object { - "data": Object { - "agent.name": "python", - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "serviceAnomalyStats": Object { - "actualValue": 47107.7692307692, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - Object { - "data": Object { - "id": ">elasticsearch", - "label": "elasticsearch", - "span.destination.service.resource": "elasticsearch", - "span.subtype": "elasticsearch", - "span.type": "db", - }, - }, - Object { - "data": Object { - "id": ">redis", - "label": "redis", - "span.destination.service.resource": "redis", - "span.subtype": "redis", - "span.type": "db", - }, - }, - Object { - "data": Object { - "agent.name": "dotnet", - "id": "opbeans-dotnet", - "service.environment": null, - "service.name": "opbeans-dotnet", - }, - }, - Object { - "data": Object { - "groupedConnections": Array [ - Object { - "id": ">a18132920325.cdn.optimizely.com:443", - "label": "a18132920325.cdn.optimizely.com:443", - "span.destination.service.resource": "a18132920325.cdn.optimizely.com:443", - "span.subtype": "iframe", - "span.type": "resource", - }, - Object { - "id": ">cdn.optimizely.com:443", - "label": "cdn.optimizely.com:443", - "span.destination.service.resource": "cdn.optimizely.com:443", - "span.subtype": "script", - "span.type": "resource", - }, - Object { - "id": ">fonts.googleapis.com:443", - "label": "fonts.googleapis.com:443", - "span.destination.service.resource": "fonts.googleapis.com:443", - "span.subtype": "link", - "span.type": "resource", - }, - Object { - "id": ">images.contentstack.io:443", - "label": "images.contentstack.io:443", - "span.destination.service.resource": "images.contentstack.io:443", - "span.subtype": "css", - "span.type": "resource", - }, - Object { - "id": ">info.elastic.co:443", - "label": "info.elastic.co:443", - "span.destination.service.resource": "info.elastic.co:443", - "span.subtype": "script", - "span.type": "resource", - }, - Object { - "id": ">p.typekit.net:443", - "label": "p.typekit.net:443", - "span.destination.service.resource": "p.typekit.net:443", - "span.subtype": "css", - "span.type": "resource", - }, - Object { - "id": ">static-www.elastic.co:443", - "label": "static-www.elastic.co:443", - "span.destination.service.resource": "static-www.elastic.co:443", - "span.subtype": "img", - "span.type": "resource", - }, - Object { - "id": ">use.typekit.net:443", - "label": "use.typekit.net:443", - "span.destination.service.resource": "use.typekit.net:443", - "span.subtype": "link", - "span.type": "resource", - }, - Object { - "id": ">www.elastic.co:443", - "label": "www.elastic.co:443", - "span.destination.service.resource": "www.elastic.co:443", - "span.subtype": "browser-timing", - "span.type": "hard-navigation", - }, - ], - "id": "resourceGroup{elastic-co-frontend}", - "label": "9 resources", - "span.type": "external", - }, - }, - Object { - "data": Object { - "id": "opbeans-go~>postgresql", - "source": "opbeans-go", - "sourceData": Object { - "agent.name": "go", - "id": "opbeans-go", - "service.environment": "testing", - "service.name": "opbeans-go", - }, - "target": ">postgresql", - "targetData": Object { - "id": ">postgresql", - "label": "postgresql", - "span.destination.service.resource": "postgresql", - "span.subtype": "postgresql", - "span.type": "db", - }, - }, - }, - Object { - "data": Object { - "bidirectional": true, - "id": "opbeans-go~opbeans-node", - "source": "opbeans-go", - "sourceData": Object { - "agent.name": "go", - "id": "opbeans-go", - "service.environment": "testing", - "service.name": "opbeans-go", - }, - "target": "opbeans-node", - "targetData": Object { - "agent.name": "nodejs", - "id": "opbeans-node", - "service.environment": "testing", - "service.name": "opbeans-node", - }, - }, - }, - Object { - "data": Object { - "bidirectional": true, - "id": "opbeans-go~opbeans-python", - "source": "opbeans-go", - "sourceData": Object { - "agent.name": "go", - "id": "opbeans-go", - "service.environment": "testing", - "service.name": "opbeans-go", - }, - "target": "opbeans-python", - "targetData": Object { - "agent.name": "python", - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "serviceAnomalyStats": Object { - "actualValue": 47107.7692307692, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-java~>postgresql", - "source": "opbeans-java", - "sourceData": Object { - "agent.name": "java", - "id": "opbeans-java", - "service.environment": "production", - "service.name": "opbeans-java", - "serviceAnomalyStats": Object { - "actualValue": 559010.6, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": ">postgresql", - "targetData": Object { - "id": ">postgresql", - "label": "postgresql", - "span.destination.service.resource": "postgresql", - "span.subtype": "postgresql", - "span.type": "db", - }, - }, - }, - Object { - "data": Object { - "bidirectional": true, - "id": "opbeans-java~opbeans-node", - "source": "opbeans-java", - "sourceData": Object { - "agent.name": "java", - "id": "opbeans-java", - "service.environment": "production", - "service.name": "opbeans-java", - "serviceAnomalyStats": Object { - "actualValue": 559010.6, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": "opbeans-node", - "targetData": Object { - "agent.name": "nodejs", - "id": "opbeans-node", - "service.environment": "testing", - "service.name": "opbeans-node", - }, - }, - }, - Object { - "data": Object { - "bidirectional": true, - "id": "opbeans-java~opbeans-ruby", - "source": "opbeans-java", - "sourceData": Object { - "agent.name": "java", - "id": "opbeans-java", - "service.environment": "production", - "service.name": "opbeans-java", - "serviceAnomalyStats": Object { - "actualValue": 559010.6, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": "opbeans-ruby", - "targetData": Object { - "agent.name": "ruby", - "id": "opbeans-ruby", - "service.environment": "production", - "service.name": "opbeans-ruby", - "serviceAnomalyStats": Object { - "actualValue": 141536.936507937, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-node~>postgresql", - "source": "opbeans-node", - "sourceData": Object { - "agent.name": "nodejs", - "id": "opbeans-node", - "service.environment": "testing", - "service.name": "opbeans-node", - }, - "target": ">postgresql", - "targetData": Object { - "id": ">postgresql", - "label": "postgresql", - "span.destination.service.resource": "postgresql", - "span.subtype": "postgresql", - "span.type": "db", - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-node~opbeans-go", - "isInverseEdge": true, - "source": "opbeans-node", - "sourceData": Object { - "agent.name": "nodejs", - "id": "opbeans-node", - "service.environment": "testing", - "service.name": "opbeans-node", - }, - "target": "opbeans-go", - "targetData": Object { - "agent.name": "go", - "id": "opbeans-go", - "service.environment": "testing", - "service.name": "opbeans-go", - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-node~opbeans-java", - "isInverseEdge": true, - "source": "opbeans-node", - "sourceData": Object { - "agent.name": "nodejs", - "id": "opbeans-node", - "service.environment": "testing", - "service.name": "opbeans-node", - }, - "target": "opbeans-java", - "targetData": Object { - "agent.name": "java", - "id": "opbeans-java", - "service.environment": "production", - "service.name": "opbeans-java", - "serviceAnomalyStats": Object { - "actualValue": 559010.6, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - }, - Object { - "data": Object { - "bidirectional": true, - "id": "opbeans-node~opbeans-python", - "source": "opbeans-node", - "sourceData": Object { - "agent.name": "nodejs", - "id": "opbeans-node", - "service.environment": "testing", - "service.name": "opbeans-node", - }, - "target": "opbeans-python", - "targetData": Object { - "agent.name": "python", - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "serviceAnomalyStats": Object { - "actualValue": 47107.7692307692, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - }, - Object { - "data": Object { - "bidirectional": true, - "id": "opbeans-node~opbeans-ruby", - "source": "opbeans-node", - "sourceData": Object { - "agent.name": "nodejs", - "id": "opbeans-node", - "service.environment": "testing", - "service.name": "opbeans-node", - }, - "target": "opbeans-ruby", - "targetData": Object { - "agent.name": "ruby", - "id": "opbeans-ruby", - "service.environment": "production", - "service.name": "opbeans-ruby", - "serviceAnomalyStats": Object { - "actualValue": 141536.936507937, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-python~>elasticsearch", - "source": "opbeans-python", - "sourceData": Object { - "agent.name": "python", - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "serviceAnomalyStats": Object { - "actualValue": 47107.7692307692, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": ">elasticsearch", - "targetData": Object { - "id": ">elasticsearch", - "label": "elasticsearch", - "span.destination.service.resource": "elasticsearch", - "span.subtype": "elasticsearch", - "span.type": "db", - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-python~>postgresql", - "source": "opbeans-python", - "sourceData": Object { - "agent.name": "python", - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "serviceAnomalyStats": Object { - "actualValue": 47107.7692307692, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": ">postgresql", - "targetData": Object { - "id": ">postgresql", - "label": "postgresql", - "span.destination.service.resource": "postgresql", - "span.subtype": "postgresql", - "span.type": "db", - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-python~>redis", - "source": "opbeans-python", - "sourceData": Object { - "agent.name": "python", - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "serviceAnomalyStats": Object { - "actualValue": 47107.7692307692, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": ">redis", - "targetData": Object { - "id": ">redis", - "label": "redis", - "span.destination.service.resource": "redis", - "span.subtype": "redis", - "span.type": "db", - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-python~opbeans-go", - "isInverseEdge": true, - "source": "opbeans-python", - "sourceData": Object { - "agent.name": "python", - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "serviceAnomalyStats": Object { - "actualValue": 47107.7692307692, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": "opbeans-go", - "targetData": Object { - "agent.name": "go", - "id": "opbeans-go", - "service.environment": "testing", - "service.name": "opbeans-go", - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-python~opbeans-node", - "isInverseEdge": true, - "source": "opbeans-python", - "sourceData": Object { - "agent.name": "python", - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "serviceAnomalyStats": Object { - "actualValue": 47107.7692307692, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": "opbeans-node", - "targetData": Object { - "agent.name": "nodejs", - "id": "opbeans-node", - "service.environment": "testing", - "service.name": "opbeans-node", - }, - }, - }, - Object { - "data": Object { - "bidirectional": true, - "id": "opbeans-python~opbeans-ruby", - "source": "opbeans-python", - "sourceData": Object { - "agent.name": "python", - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "serviceAnomalyStats": Object { - "actualValue": 47107.7692307692, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": "opbeans-ruby", - "targetData": Object { - "agent.name": "ruby", - "id": "opbeans-ruby", - "service.environment": "production", - "service.name": "opbeans-ruby", - "serviceAnomalyStats": Object { - "actualValue": 141536.936507937, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-ruby~>postgresql", - "source": "opbeans-ruby", - "sourceData": Object { - "agent.name": "ruby", - "id": "opbeans-ruby", - "service.environment": "production", - "service.name": "opbeans-ruby", - "serviceAnomalyStats": Object { - "actualValue": 141536.936507937, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": ">postgresql", - "targetData": Object { - "id": ">postgresql", - "label": "postgresql", - "span.destination.service.resource": "postgresql", - "span.subtype": "postgresql", - "span.type": "db", - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-ruby~opbeans-go", - "source": "opbeans-ruby", - "sourceData": Object { - "agent.name": "ruby", - "id": "opbeans-ruby", - "service.environment": "production", - "service.name": "opbeans-ruby", - "serviceAnomalyStats": Object { - "actualValue": 141536.936507937, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": "opbeans-go", - "targetData": Object { - "agent.name": "go", - "id": "opbeans-go", - "service.environment": "testing", - "service.name": "opbeans-go", - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-ruby~opbeans-java", - "isInverseEdge": true, - "source": "opbeans-ruby", - "sourceData": Object { - "agent.name": "ruby", - "id": "opbeans-ruby", - "service.environment": "production", - "service.name": "opbeans-ruby", - "serviceAnomalyStats": Object { - "actualValue": 141536.936507937, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": "opbeans-java", - "targetData": Object { - "agent.name": "java", - "id": "opbeans-java", - "service.environment": "production", - "service.name": "opbeans-java", - "serviceAnomalyStats": Object { - "actualValue": 559010.6, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-ruby~opbeans-node", - "isInverseEdge": true, - "source": "opbeans-ruby", - "sourceData": Object { - "agent.name": "ruby", - "id": "opbeans-ruby", - "service.environment": "production", - "service.name": "opbeans-ruby", - "serviceAnomalyStats": Object { - "actualValue": 141536.936507937, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": "opbeans-node", - "targetData": Object { - "agent.name": "nodejs", - "id": "opbeans-node", - "service.environment": "testing", - "service.name": "opbeans-node", - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-ruby~opbeans-python", - "isInverseEdge": true, - "source": "opbeans-ruby", - "sourceData": Object { - "agent.name": "ruby", - "id": "opbeans-ruby", - "service.environment": "production", - "service.name": "opbeans-ruby", - "serviceAnomalyStats": Object { - "actualValue": 141536.936507937, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - "target": "opbeans-python", - "targetData": Object { - "agent.name": "python", - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "serviceAnomalyStats": Object { - "actualValue": 47107.7692307692, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-rum~opbeans-go", - "source": "opbeans-rum", - "sourceData": Object { - "agent.name": "rum-js", - "id": "opbeans-rum", - "service.environment": "testing", - "service.name": "opbeans-rum", - "serviceAnomalyStats": Object { - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-environment_not_defined-7ed6-high_mean_transaction_duration", - "transactionType": "page-load", - }, - }, - "target": "opbeans-go", - "targetData": Object { - "agent.name": "go", - "id": "opbeans-go", - "service.environment": "testing", - "service.name": "opbeans-go", - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-rum~opbeans-java", - "source": "opbeans-rum", - "sourceData": Object { - "agent.name": "rum-js", - "id": "opbeans-rum", - "service.environment": "testing", - "service.name": "opbeans-rum", - "serviceAnomalyStats": Object { - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-environment_not_defined-7ed6-high_mean_transaction_duration", - "transactionType": "page-load", - }, - }, - "target": "opbeans-java", - "targetData": Object { - "agent.name": "java", - "id": "opbeans-java", - "service.environment": "production", - "service.name": "opbeans-java", - "serviceAnomalyStats": Object { - "actualValue": 559010.6, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-rum~opbeans-node", - "source": "opbeans-rum", - "sourceData": Object { - "agent.name": "rum-js", - "id": "opbeans-rum", - "service.environment": "testing", - "service.name": "opbeans-rum", - "serviceAnomalyStats": Object { - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-environment_not_defined-7ed6-high_mean_transaction_duration", - "transactionType": "page-load", - }, - }, - "target": "opbeans-node", - "targetData": Object { - "agent.name": "nodejs", - "id": "opbeans-node", - "service.environment": "testing", - "service.name": "opbeans-node", - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-rum~opbeans-python", - "source": "opbeans-rum", - "sourceData": Object { - "agent.name": "rum-js", - "id": "opbeans-rum", - "service.environment": "testing", - "service.name": "opbeans-rum", - "serviceAnomalyStats": Object { - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-environment_not_defined-7ed6-high_mean_transaction_duration", - "transactionType": "page-load", - }, - }, - "target": "opbeans-python", - "targetData": Object { - "agent.name": "python", - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "serviceAnomalyStats": Object { - "actualValue": 47107.7692307692, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - }, - Object { - "data": Object { - "id": "opbeans-rum~opbeans-ruby", - "source": "opbeans-rum", - "sourceData": Object { - "agent.name": "rum-js", - "id": "opbeans-rum", - "service.environment": "testing", - "service.name": "opbeans-rum", - "serviceAnomalyStats": Object { - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-environment_not_defined-7ed6-high_mean_transaction_duration", - "transactionType": "page-load", - }, - }, - "target": "opbeans-ruby", - "targetData": Object { - "agent.name": "ruby", - "id": "opbeans-ruby", - "service.environment": "production", - "service.name": "opbeans-ruby", - "serviceAnomalyStats": Object { - "actualValue": 141536.936507937, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-production-229a-high_mean_transaction_duration", - "transactionType": "request", - }, - }, - }, - }, - Object { - "data": Object { - "id": "elastic-co-frontend~>resourceGroup{elastic-co-frontend}", - "source": "elastic-co-frontend", - "target": "resourceGroup{elastic-co-frontend}", - }, - }, - ], -} -`; diff --git a/x-pack/test/apm_api_integration/trial/tests/services/__snapshots__/transaction_groups_charts.snap b/x-pack/test/apm_api_integration/trial/tests/services/__snapshots__/transaction_groups_charts.snap deleted file mode 100644 index 8169e73202fbc..0000000000000 --- a/x-pack/test/apm_api_integration/trial/tests/services/__snapshots__/transaction_groups_charts.snap +++ /dev/null @@ -1,43 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`APM Transaction Overview when data is loaded and fetching transaction groups charts with uiFilters when not defined environments selected should return the correct anomaly boundaries 1`] = `Array []`; - -exports[`APM Transaction Overview when data is loaded and fetching transaction groups charts with uiFilters with environment selected and empty kuery filter should return a non-empty anomaly series 1`] = ` -Array [ - Object { - "x": 1601389800000, - "y": 1206111.33487531, - "y0": 10555.1290143587, - }, - Object { - "x": 1601390700000, - "y": 1223987.49321778, - "y0": 10177.4677901726, - }, - Object { - "x": 1601391600000, - "y": 1223987.49321778, - "y0": 10177.4677901726, - }, -] -`; - -exports[`APM Transaction Overview when data is loaded and fetching transaction groups charts with uiFilters with environment selected in uiFilters should return a non-empty anomaly series 1`] = ` -Array [ - Object { - "x": 1601389800000, - "y": 1206111.33487531, - "y0": 10555.1290143587, - }, - Object { - "x": 1601390700000, - "y": 1223987.49321778, - "y0": 10177.4677901726, - }, - Object { - "x": 1601391600000, - "y": 1223987.49321778, - "y0": 10177.4677901726, - }, -] -`; From 4f3d72b413505cfa9e4475e7b02d3a46fff85ef9 Mon Sep 17 00:00:00 2001 From: Scotty Bollinger Date: Wed, 2 Dec 2020 15:34:19 -0600 Subject: [PATCH 20/40] [Enterprise Search] Move schema types to shared (#84822) * Move schema types to shared We use the Schema types in Workplace Search as well, so moving these to shared. Also, we have a component called IndexingStatus so reverting to the prefixed IIndexingStatus interface name * Fix misspelled interface --- .../components/engine/engine_logic.ts | 4 +-- .../app_search/components/engine/types.ts | 4 +-- .../app_search/components/schema/types.ts | 34 ------------------- .../public/applications/shared/types.ts | 23 +++++++++++++ 4 files changed, 27 insertions(+), 38 deletions(-) delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/schema/types.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.ts index 2e7595e3ee87b..51896becd8703 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.ts @@ -8,7 +8,7 @@ import { kea, MakeLogicType } from 'kea'; import { HttpLogic } from '../../../shared/http'; -import { IndexingStatus } from '../schema/types'; +import { IIndexingStatus } from '../../../shared/types'; import { EngineDetails } from './types'; interface EngineValues { @@ -25,7 +25,7 @@ interface EngineValues { interface EngineActions { setEngineData(engine: EngineDetails): { engine: EngineDetails }; setEngineName(engineName: string): { engineName: string }; - setIndexingStatus(activeReindexJob: IndexingStatus): { activeReindexJob: IndexingStatus }; + setIndexingStatus(activeReindexJob: IIndexingStatus): { activeReindexJob: IIndexingStatus }; setEngineNotFound(notFound: boolean): { notFound: boolean }; clearEngine(): void; initializeEngine(): void; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/types.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/types.ts index 635d1136291aa..99ad19fea0619 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/types.ts @@ -5,7 +5,7 @@ */ import { ApiToken } from '../credentials/types'; -import { Schema, SchemaConflicts, IndexingStatus } from '../schema/types'; +import { Schema, SchemaConflicts, IIndexingStatus } from '../../../shared/types'; export interface Engine { name: string; @@ -26,7 +26,7 @@ export interface EngineDetails extends Engine { schema: Schema; schemaConflicts?: SchemaConflicts; unconfirmedFields?: string[]; - activeReindexJob?: IndexingStatus; + activeReindexJob?: IIndexingStatus; invalidBoosts: boolean; sample?: boolean; isMeta: boolean; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/schema/types.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/schema/types.ts deleted file mode 100644 index 84f402dd3b95f..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/schema/types.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export type SchemaTypes = 'text' | 'number' | 'geolocation' | 'date'; - -export interface Schema { - [key: string]: SchemaTypes; -} - -// this is a mapping of schema field types ("string", "number", "geolocation", "date") to the names -// of source engines which utilize that type -export type SchemaConflictFieldTypes = { - [key in SchemaTypes]: string[]; -}; - -export interface SchemaConflict { - fieldTypes: SchemaConflictFieldTypes; - resolution?: string; -} - -// For now these values are ISchemaConflictFieldTypes, but in the near future will be ISchemaConflict -// once we implement schema conflict resolution -export interface SchemaConflicts { - [key: string]: SchemaConflictFieldTypes; -} - -export interface IndexingStatus { - percentageComplete: number; - numDocumentsWithErrors: number; - activeReindexJobId: number; -} diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/types.ts b/x-pack/plugins/enterprise_search/public/applications/shared/types.ts index 3866d1a7199e4..38a6187d290b5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/types.ts @@ -4,6 +4,29 @@ * you may not use this file except in compliance with the Elastic License. */ +export type SchemaTypes = 'text' | 'number' | 'geolocation' | 'date'; + +export interface Schema { + [key: string]: SchemaTypes; +} + +// this is a mapping of schema field types ("string", "number", "geolocation", "date") to the names +// of source engines which utilize that type +export type SchemaConflictFieldTypes = { + [key in SchemaTypes]: string[]; +}; + +export interface SchemaConflict { + fieldTypes: SchemaConflictFieldTypes; + resolution?: string; +} + +// For now these values are ISchemaConflictFieldTypes, but in the near future will be ISchemaConflict +// once we implement schema conflict resolution +export interface SchemaConflicts { + [key: string]: SchemaConflictFieldTypes; +} + export interface IIndexingStatus { percentageComplete: number; numDocumentsWithErrors: number; From d47c70cd53ffa818514be5acbb1f31ce207ba2f7 Mon Sep 17 00:00:00 2001 From: Marshall Main <55718608+marshallmain@users.noreply.github.com> Date: Wed, 2 Dec 2020 17:14:19 -0500 Subject: [PATCH 21/40] [Security Solution][Exceptions] Implement exceptions for ML rules (#84006) * Implement exceptions for ML rules * Remove unused import * Better implicit types * Retrieve ML rule index pattern for exception field suggestions and autocomplete * Add ML job logic to edit exception modal * Remove unnecessary logic change Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../detection_engine/get_query_filter.test.ts | 72 +++++--- .../detection_engine/get_query_filter.ts | 79 +++++---- .../exceptions/add_exception_modal/index.tsx | 23 ++- .../exceptions/edit_exception_modal/index.tsx | 149 +++++++++------- .../common/components/ml/api/get_jobs.ts | 35 ++++ .../components/ml/hooks/use_get_jobs.ts | 59 +++++++ .../timeline_actions/alert_context_menu.tsx | 19 +- .../rules/step_about_rule/index.tsx | 6 +- .../detection_engine/rules/details/index.tsx | 3 +- .../signals/__mocks__/es_results.ts | 8 +- .../signals/bulk_create_ml_signals.ts | 2 +- .../signals/filter_events_with_list.test.ts | 162 ++++++++++++++++++ .../signals/filter_events_with_list.ts | 71 +++++--- .../signals/find_ml_signals.ts | 4 + .../signals/signal_rule_alert_type.ts | 21 ++- .../server/lib/machine_learning/index.test.ts | 2 + .../server/lib/machine_learning/index.ts | 16 +- 17 files changed, 552 insertions(+), 179 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/common/components/ml/api/get_jobs.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/ml/hooks/use_get_jobs.ts diff --git a/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.test.ts b/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.test.ts index 8ff75b25388b0..4fff99b09d4ad 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.test.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.test.ts @@ -7,6 +7,7 @@ import { getQueryFilter, buildExceptionFilter, buildEqlSearchRequest } from './get_query_filter'; import { Filter, EsQueryConfig } from 'src/plugins/data/public'; import { getExceptionListItemSchemaMock } from '../../../lists/common/schemas/response/exception_list_item_schema.mock'; +import { ExceptionListItemSchema } from '../shared_imports'; describe('get_filter', () => { describe('getQueryFilter', () => { @@ -919,19 +920,27 @@ describe('get_filter', () => { dateFormatTZ: 'Zulu', }; test('it should build a filter without chunking exception items', () => { - const exceptionFilter = buildExceptionFilter( - [ - { language: 'kuery', query: 'host.name: linux and some.field: value' }, - { language: 'kuery', query: 'user.name: name' }, + const exceptionItem1: ExceptionListItemSchema = { + ...getExceptionListItemSchemaMock(), + entries: [ + { field: 'host.name', operator: 'included', type: 'match', value: 'linux' }, + { field: 'some.field', operator: 'included', type: 'match', value: 'value' }, ], - { + }; + const exceptionItem2: ExceptionListItemSchema = { + ...getExceptionListItemSchemaMock(), + entries: [{ field: 'user.name', operator: 'included', type: 'match', value: 'name' }], + }; + const exceptionFilter = buildExceptionFilter({ + lists: [exceptionItem1, exceptionItem2], + config, + excludeExceptions: true, + chunkSize: 2, + indexPattern: { fields: [], title: 'auditbeat-*', }, - config, - true, - 2 - ); + }); expect(exceptionFilter).toEqual({ meta: { alias: null, @@ -949,7 +958,7 @@ describe('get_filter', () => { minimum_should_match: 1, should: [ { - match: { + match_phrase: { 'host.name': 'linux', }, }, @@ -961,7 +970,7 @@ describe('get_filter', () => { minimum_should_match: 1, should: [ { - match: { + match_phrase: { 'some.field': 'value', }, }, @@ -976,7 +985,7 @@ describe('get_filter', () => { minimum_should_match: 1, should: [ { - match: { + match_phrase: { 'user.name': 'name', }, }, @@ -990,20 +999,31 @@ describe('get_filter', () => { }); test('it should properly chunk exception items', () => { - const exceptionFilter = buildExceptionFilter( - [ - { language: 'kuery', query: 'host.name: linux and some.field: value' }, - { language: 'kuery', query: 'user.name: name' }, - { language: 'kuery', query: 'file.path: /safe/path' }, + const exceptionItem1: ExceptionListItemSchema = { + ...getExceptionListItemSchemaMock(), + entries: [ + { field: 'host.name', operator: 'included', type: 'match', value: 'linux' }, + { field: 'some.field', operator: 'included', type: 'match', value: 'value' }, ], - { + }; + const exceptionItem2: ExceptionListItemSchema = { + ...getExceptionListItemSchemaMock(), + entries: [{ field: 'user.name', operator: 'included', type: 'match', value: 'name' }], + }; + const exceptionItem3: ExceptionListItemSchema = { + ...getExceptionListItemSchemaMock(), + entries: [{ field: 'file.path', operator: 'included', type: 'match', value: '/safe/path' }], + }; + const exceptionFilter = buildExceptionFilter({ + lists: [exceptionItem1, exceptionItem2, exceptionItem3], + config, + excludeExceptions: true, + chunkSize: 2, + indexPattern: { fields: [], title: 'auditbeat-*', }, - config, - true, - 2 - ); + }); expect(exceptionFilter).toEqual({ meta: { alias: null, @@ -1024,7 +1044,7 @@ describe('get_filter', () => { minimum_should_match: 1, should: [ { - match: { + match_phrase: { 'host.name': 'linux', }, }, @@ -1036,7 +1056,7 @@ describe('get_filter', () => { minimum_should_match: 1, should: [ { - match: { + match_phrase: { 'some.field': 'value', }, }, @@ -1051,7 +1071,7 @@ describe('get_filter', () => { minimum_should_match: 1, should: [ { - match: { + match_phrase: { 'user.name': 'name', }, }, @@ -1069,7 +1089,7 @@ describe('get_filter', () => { minimum_should_match: 1, should: [ { - match: { + match_phrase: { 'file.path': '/safe/path', }, }, diff --git a/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts b/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts index 73638fc48f381..fcea90402d89d 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts @@ -6,7 +6,6 @@ import { Filter, - Query, IIndexPattern, isFilterDisabled, buildEsQuery, @@ -18,15 +17,10 @@ import { } from '../../../lists/common/schemas'; import { ESBoolQuery } from '../typed_json'; import { buildExceptionListQueries } from './build_exceptions_query'; -import { - Query as QueryString, - Language, - Index, - TimestampOverrideOrUndefined, -} from './schemas/common/schemas'; +import { Query, Language, Index, TimestampOverrideOrUndefined } from './schemas/common/schemas'; export const getQueryFilter = ( - query: QueryString, + query: Query, language: Language, filters: Array>, index: Index, @@ -53,19 +47,18 @@ export const getQueryFilter = ( * buildEsQuery, this allows us to offer nested queries * regardless */ - const exceptionQueries = buildExceptionListQueries({ language: 'kuery', lists }); - if (exceptionQueries.length > 0) { - // Assume that `indices.query.bool.max_clause_count` is at least 1024 (the default value), - // allowing us to make 1024-item chunks of exception list items. - // Discussion at https://issues.apache.org/jira/browse/LUCENE-4835 indicates that 1024 is a - // very conservative value. - const exceptionFilter = buildExceptionFilter( - exceptionQueries, - indexPattern, - config, - excludeExceptions, - 1024 - ); + // Assume that `indices.query.bool.max_clause_count` is at least 1024 (the default value), + // allowing us to make 1024-item chunks of exception list items. + // Discussion at https://issues.apache.org/jira/browse/LUCENE-4835 indicates that 1024 is a + // very conservative value. + const exceptionFilter = buildExceptionFilter({ + lists, + config, + excludeExceptions, + chunkSize: 1024, + indexPattern, + }); + if (exceptionFilter !== undefined) { enabledFilters.push(exceptionFilter); } const initialQuery = { query, language }; @@ -101,15 +94,17 @@ export const buildEqlSearchRequest = ( ignoreFilterIfFieldNotInIndex: false, dateFormatTZ: 'Zulu', }; - const exceptionQueries = buildExceptionListQueries({ language: 'kuery', lists: exceptionLists }); - let exceptionFilter: Filter | undefined; - if (exceptionQueries.length > 0) { - // Assume that `indices.query.bool.max_clause_count` is at least 1024 (the default value), - // allowing us to make 1024-item chunks of exception list items. - // Discussion at https://issues.apache.org/jira/browse/LUCENE-4835 indicates that 1024 is a - // very conservative value. - exceptionFilter = buildExceptionFilter(exceptionQueries, indexPattern, config, true, 1024); - } + // Assume that `indices.query.bool.max_clause_count` is at least 1024 (the default value), + // allowing us to make 1024-item chunks of exception list items. + // Discussion at https://issues.apache.org/jira/browse/LUCENE-4835 indicates that 1024 is a + // very conservative value. + const exceptionFilter = buildExceptionFilter({ + lists: exceptionLists, + config, + excludeExceptions: true, + chunkSize: 1024, + indexPattern, + }); const indexString = index.join(); const requestFilter: unknown[] = [ { @@ -154,13 +149,23 @@ export const buildEqlSearchRequest = ( } }; -export const buildExceptionFilter = ( - exceptionQueries: Query[], - indexPattern: IIndexPattern, - config: EsQueryConfig, - excludeExceptions: boolean, - chunkSize: number -) => { +export const buildExceptionFilter = ({ + lists, + config, + excludeExceptions, + chunkSize, + indexPattern, +}: { + lists: Array; + config: EsQueryConfig; + excludeExceptions: boolean; + chunkSize: number; + indexPattern?: IIndexPattern; +}) => { + const exceptionQueries = buildExceptionListQueries({ language: 'kuery', lists }); + if (exceptionQueries.length === 0) { + return undefined; + } const exceptionFilter: Filter = { meta: { alias: null, diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx index b2f8426413b12..0bbe4c71ef5a8 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +/* eslint complexity: ["error", 30]*/ + import React, { memo, useEffect, useState, useCallback, useMemo } from 'react'; import styled, { css } from 'styled-components'; import { @@ -53,6 +55,7 @@ import { import { ErrorInfo, ErrorCallout } from '../error_callout'; import { ExceptionsBuilderExceptionItem } from '../types'; import { useFetchIndex } from '../../../containers/source'; +import { useGetInstalledJob } from '../../ml/hooks/use_get_jobs'; export interface AddExceptionModalProps { ruleName: string; @@ -108,7 +111,7 @@ export const AddExceptionModal = memo(function AddExceptionModal({ const { http } = useKibana().services; const [errorsExist, setErrorExists] = useState(false); const [comment, setComment] = useState(''); - const { rule: maybeRule } = useRuleAsync(ruleId); + const { rule: maybeRule, loading: isRuleLoading } = useRuleAsync(ruleId); const [shouldCloseAlert, setShouldCloseAlert] = useState(false); const [shouldBulkCloseAlert, setShouldBulkCloseAlert] = useState(false); const [shouldDisableBulkClose, setShouldDisableBulkClose] = useState(false); @@ -124,8 +127,22 @@ export const AddExceptionModal = memo(function AddExceptionModal({ const [isSignalIndexPatternLoading, { indexPatterns: signalIndexPatterns }] = useFetchIndex( memoSignalIndexName ); - const [isIndexPatternLoading, { indexPatterns }] = useFetchIndex(ruleIndices); + const memoMlJobIds = useMemo( + () => (maybeRule?.machine_learning_job_id != null ? [maybeRule.machine_learning_job_id] : []), + [maybeRule] + ); + const { loading: mlJobLoading, jobs } = useGetInstalledJob(memoMlJobIds); + + const memoRuleIndices = useMemo(() => { + if (jobs.length > 0) { + return jobs[0].results_index_name ? [`.ml-anomalies-${jobs[0].results_index_name}`] : []; + } else { + return ruleIndices; + } + }, [jobs, ruleIndices]); + + const [isIndexPatternLoading, { indexPatterns }] = useFetchIndex(memoRuleIndices); const onError = useCallback( (error: Error): void => { addError(error, { title: i18n.ADD_EXCEPTION_ERROR }); @@ -364,6 +381,8 @@ export const AddExceptionModal = memo(function AddExceptionModal({ !isSignalIndexPatternLoading && !isLoadingExceptionList && !isIndexPatternLoading && + !isRuleLoading && + !mlJobLoading && ruleExceptionList && ( <> diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx index ab0c566aa55c6..e97f745d6f979 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx @@ -47,6 +47,7 @@ import { } from '../helpers'; import { Loader } from '../../loader'; import { ErrorInfo, ErrorCallout } from '../error_callout'; +import { useGetInstalledJob } from '../../ml/hooks/use_get_jobs'; interface EditExceptionModalProps { ruleName: string; @@ -100,7 +101,7 @@ export const EditExceptionModal = memo(function EditExceptionModal({ const { http } = useKibana().services; const [comment, setComment] = useState(''); const [errorsExist, setErrorExists] = useState(false); - const { rule: maybeRule } = useRuleAsync(ruleId); + const { rule: maybeRule, loading: isRuleLoading } = useRuleAsync(ruleId); const [updateError, setUpdateError] = useState(null); const [hasVersionConflict, setHasVersionConflict] = useState(false); const [shouldBulkCloseAlert, setShouldBulkCloseAlert] = useState(false); @@ -117,7 +118,21 @@ export const EditExceptionModal = memo(function EditExceptionModal({ memoSignalIndexName ); - const [isIndexPatternLoading, { indexPatterns }] = useFetchIndex(ruleIndices); + const memoMlJobIds = useMemo( + () => (maybeRule?.machine_learning_job_id != null ? [maybeRule.machine_learning_job_id] : []), + [maybeRule] + ); + const { loading: mlJobLoading, jobs } = useGetInstalledJob(memoMlJobIds); + + const memoRuleIndices = useMemo(() => { + if (jobs.length > 0) { + return jobs[0].results_index_name ? [`.ml-anomalies-${jobs[0].results_index_name}`] : []; + } else { + return ruleIndices; + } + }, [jobs, ruleIndices]); + + const [isIndexPatternLoading, { indexPatterns }] = useFetchIndex(memoRuleIndices); const handleExceptionUpdateError = useCallback( (error: Error, statusCode: number | null, message: string | null) => { @@ -280,69 +295,75 @@ export const EditExceptionModal = memo(function EditExceptionModal({ {(addExceptionIsLoading || isIndexPatternLoading || isSignalIndexLoading) && ( )} - {!isSignalIndexLoading && !addExceptionIsLoading && !isIndexPatternLoading && ( - <> - - {isRuleEQLSequenceStatement && ( - <> - - - - )} - {i18n.EXCEPTION_BUILDER_INFO} - - - - - - - - - - - + + {isRuleEQLSequenceStatement && ( + <> + + + + )} + {i18n.EXCEPTION_BUILDER_INFO} + + - - {exceptionListType === 'endpoint' && ( - <> - - - {i18n.ENDPOINT_QUARANTINE_TEXT} - - - )} - - - )} + + + + + + + + + + + {exceptionListType === 'endpoint' && ( + <> + + + {i18n.ENDPOINT_QUARANTINE_TEXT} + + + )} + + + )} {updateError != null && ( => + http.fetch('/api/ml/jobs/jobs', { + method: 'POST', + body: JSON.stringify({ jobIds }), + asSystemRequest: true, + signal, + }); diff --git a/x-pack/plugins/security_solution/public/common/components/ml/hooks/use_get_jobs.ts b/x-pack/plugins/security_solution/public/common/components/ml/hooks/use_get_jobs.ts new file mode 100644 index 0000000000000..4d7b342773d6c --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/ml/hooks/use_get_jobs.ts @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useEffect, useState } from 'react'; +import { useAsync, withOptionalSignal } from '../../../../shared_imports'; +import { getJobs } from '../api/get_jobs'; +import { CombinedJobWithStats } from '../../../../../../ml/common/types/anomaly_detection_jobs'; + +import { hasMlUserPermissions } from '../../../../../common/machine_learning/has_ml_user_permissions'; +import { hasMlLicense } from '../../../../../common/machine_learning/has_ml_license'; +import { useAppToasts } from '../../../hooks/use_app_toasts'; +import { useHttp } from '../../../lib/kibana'; +import { useMlCapabilities } from './use_ml_capabilities'; +import * as i18n from '../translations'; + +const _getJobs = withOptionalSignal(getJobs); + +export const useGetJobs = () => useAsync(_getJobs); + +export interface UseGetInstalledJobReturn { + loading: boolean; + jobs: CombinedJobWithStats[]; + isMlUser: boolean; + isLicensed: boolean; +} + +export const useGetInstalledJob = (jobIds: string[]): UseGetInstalledJobReturn => { + const [jobs, setJobs] = useState([]); + const { addError } = useAppToasts(); + const mlCapabilities = useMlCapabilities(); + const http = useHttp(); + const { error, loading, result, start } = useGetJobs(); + + const isMlUser = hasMlUserPermissions(mlCapabilities); + const isLicensed = hasMlLicense(mlCapabilities); + + useEffect(() => { + if (isMlUser && isLicensed && jobIds.length > 0) { + start({ http, jobIds }); + } + }, [http, isMlUser, isLicensed, start, jobIds]); + + useEffect(() => { + if (result) { + setJobs(result); + } + }, [result]); + + useEffect(() => { + if (error) { + addError(error, { title: i18n.SIEM_JOB_FETCH_FAILURE }); + } + }, [addError, error]); + + return { isLicensed, isMlUser, jobs, loading }; +}; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx index cf8204478a955..9eb0a97a1c9a2 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx @@ -21,7 +21,6 @@ import { TimelineId } from '../../../../../common/types/timeline'; import { DEFAULT_INDEX_PATTERN } from '../../../../../common/constants'; import { Status, Type } from '../../../../../common/detection_engine/schemas/common/schemas'; import { isThresholdRule } from '../../../../../common/detection_engine/utils'; -import { isMlRule } from '../../../../../common/machine_learning/helpers'; import { timelineActions } from '../../../../timelines/store/timeline'; import { EventsTd, EventsTdContent } from '../../../../timelines/components/timeline/styles'; import { DEFAULT_ICON_BUTTON_WIDTH } from '../../../../timelines/components/timeline/helpers'; @@ -75,11 +74,17 @@ const AlertContextMenuComponent: React.FC = ({ '', [ecsRowData] ); - const ruleIndices = useMemo( - (): string[] => - (ecsRowData.signal?.rule && ecsRowData.signal.rule.index) ?? DEFAULT_INDEX_PATTERN, - [ecsRowData] - ); + const ruleIndices = useMemo((): string[] => { + if ( + ecsRowData.signal?.rule && + ecsRowData.signal.rule.index && + ecsRowData.signal.rule.index.length > 0 + ) { + return ecsRowData.signal.rule.index; + } else { + return DEFAULT_INDEX_PATTERN; + } + }, [ecsRowData]); const { addWarning } = useAppToasts(); @@ -321,7 +326,7 @@ const AlertContextMenuComponent: React.FC = ({ const areExceptionsAllowed = useMemo((): boolean => { const ruleTypes = getOr([], 'signal.rule.type', ecsRowData); const [ruleType] = ruleTypes as Type[]; - return !isMlRule(ruleType) && !isThresholdRule(ruleType); + return !isThresholdRule(ruleType); }, [ecsRowData]); // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.tsx index 2479a260872be..40b73fc7d158c 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.tsx @@ -8,7 +8,6 @@ import { EuiAccordion, EuiFlexItem, EuiSpacer, EuiFormRow } from '@elastic/eui'; import React, { FC, memo, useCallback, useEffect, useState } from 'react'; import styled from 'styled-components'; -import { isMlRule } from '../../../../../common/machine_learning/helpers'; import { isThresholdRule } from '../../../../../common/detection_engine/utils'; import { RuleStepProps, @@ -76,10 +75,7 @@ const StepAboutRuleComponent: FC = ({ const [severityValue, setSeverityValue] = useState(initialState.severity.value); const [indexPatternLoading, { indexPatterns }] = useFetchIndex(defineRuleData?.index ?? []); - const canUseExceptions = - defineRuleData?.ruleType && - !isMlRule(defineRuleData.ruleType) && - !isThresholdRule(defineRuleData.ruleType); + const canUseExceptions = defineRuleData?.ruleType && !isThresholdRule(defineRuleData.ruleType); const { form } = useForm({ defaultValue: initialState, diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx index 4866824f882cf..d04980d764831 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx @@ -80,7 +80,6 @@ import { DEFAULT_INDEX_PATTERN } from '../../../../../../common/constants'; import { useFullScreen } from '../../../../../common/containers/use_full_screen'; import { Display } from '../../../../../hosts/pages/display'; import { ExceptionListTypeEnum, ExceptionListIdentifiers } from '../../../../../shared_imports'; -import { isMlRule } from '../../../../../../common/machine_learning/helpers'; import { isThresholdRule } from '../../../../../../common/detection_engine/utils'; import { useRuleAsync } from '../../../../containers/detection_engine/rules/use_rule_async'; import { showGlobalFilters } from '../../../../../timelines/components/timeline/helpers'; @@ -104,7 +103,7 @@ enum RuleDetailTabs { } const getRuleDetailsTabs = (rule: Rule | null) => { - const canUseExceptions = rule && !isMlRule(rule.type) && !isThresholdRule(rule.type); + const canUseExceptions = rule && !isThresholdRule(rule.type); return [ { id: RuleDetailTabs.alerts, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts index 0a38bdc790b41..764604a793788 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts @@ -150,8 +150,8 @@ export const sampleDocNoSortIdNoVersion = (someUuid: string = sampleIdGuid): Sig export const sampleDocWithSortId = ( someUuid: string = sampleIdGuid, - ip?: string, - destIp?: string + ip?: string | string[], + destIp?: string | string[] ): SignalSourceHit => ({ _index: 'myFakeSignalIndex', _type: 'doc', @@ -502,8 +502,8 @@ export const repeatedSearchResultsWithSortId = ( total: number, pageSize: number, guids: string[], - ips?: string[], - destIps?: string[] + ips?: Array, + destIps?: Array ): SignalSearchResponse => ({ took: 10, timed_out: false, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.ts index 5c2dfa62e5951..d530fe10c6498 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.ts @@ -6,7 +6,6 @@ import { flow, omit } from 'lodash/fp'; import set from 'set-value'; -import { SearchResponse } from 'elasticsearch'; import { Logger } from '../../../../../../../src/core/server'; import { AlertServices } from '../../../../../alerts/server'; @@ -15,6 +14,7 @@ import { RuleTypeParams, RefreshTypes } from '../types'; import { singleBulkCreate, SingleBulkCreateResponse } from './single_bulk_create'; import { AnomalyResults, Anomaly } from '../../machine_learning'; import { BuildRuleMessage } from './rule_messages'; +import { SearchResponse } from '../../types'; interface BulkCreateMlSignalsParams { actions: RuleAlertAction[]; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filter_events_with_list.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filter_events_with_list.test.ts index 3334cc17b9050..01e7e7160e1ae 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filter_events_with_list.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filter_events_with_list.test.ts @@ -400,6 +400,87 @@ describe('filterEventsAgainstList', () => { '9.9.9.9', ]).toEqual(ipVals); }); + + it('should respond with less items in the list given one exception item with two entries of type list and array of values in document', async () => { + const exceptionItem = getExceptionListItemSchemaMock(); + exceptionItem.entries = [ + { + field: 'source.ip', + operator: 'included', + type: 'list', + list: { + id: 'ci-badguys.txt', + type: 'ip', + }, + }, + { + field: 'destination.ip', + operator: 'included', + type: 'list', + list: { + id: 'ci-badguys-again.txt', + type: 'ip', + }, + }, + ]; + + // this call represents an exception list with a value list containing ['2.2.2.2'] + (listClient.getListItemByValues as jest.Mock).mockResolvedValueOnce([ + { ...getListItemResponseMock(), value: '2.2.2.2' }, + ]); + // this call represents an exception list with a value list containing ['4.4.4.4'] + (listClient.getListItemByValues as jest.Mock).mockResolvedValueOnce([ + { ...getListItemResponseMock(), value: '4.4.4.4' }, + ]); + + const res = await filterEventsAgainstList({ + logger: mockLogger, + listClient, + exceptionsList: [exceptionItem], + eventSearchResult: repeatedSearchResultsWithSortId( + 3, + 3, + someGuids.slice(0, 3), + [ + ['1.1.1.1', '1.1.1.1'], + ['1.1.1.1', '2.2.2.2'], + ['2.2.2.2', '3.3.3.3'], + ], + [ + ['1.1.1.1', '2.2.2.2'], + ['2.2.2.2', '3.3.3.3'], + ['3.3.3.3', '4.4.4.4'], + ] + ), + buildRuleMessage, + }); + expect(listClient.getListItemByValues as jest.Mock).toHaveBeenCalledTimes(2); + expect((listClient.getListItemByValues as jest.Mock).mock.calls[0][0].value).toEqual([ + '1.1.1.1', + '2.2.2.2', + '3.3.3.3', + ]); + expect((listClient.getListItemByValues as jest.Mock).mock.calls[1][0].value).toEqual([ + '1.1.1.1', + '2.2.2.2', + '3.3.3.3', + '4.4.4.4', + ]); + expect(res.hits.hits.length).toEqual(2); + + // @ts-expect-error + const sourceIpVals = res.hits.hits.map((item) => item._source.source.ip); + expect([ + ['1.1.1.1', '1.1.1.1'], + ['1.1.1.1', '2.2.2.2'], + ]).toEqual(sourceIpVals); + // @ts-expect-error + const destIpVals = res.hits.hits.map((item) => item._source.destination.ip); + expect([ + ['1.1.1.1', '2.2.2.2'], + ['2.2.2.2', '3.3.3.3'], + ]).toEqual(destIpVals); + }); }); describe('operator type is excluded', () => { it('should respond with empty list if no items match value list', async () => { @@ -463,5 +544,86 @@ describe('filterEventsAgainstList', () => { ); expect(res.hits.hits.length).toEqual(2); }); + + it('should respond with less items in the list given one exception item with two entries of type list and array of values in document', async () => { + const exceptionItem = getExceptionListItemSchemaMock(); + exceptionItem.entries = [ + { + field: 'source.ip', + operator: 'excluded', + type: 'list', + list: { + id: 'ci-badguys.txt', + type: 'ip', + }, + }, + { + field: 'destination.ip', + operator: 'excluded', + type: 'list', + list: { + id: 'ci-badguys-again.txt', + type: 'ip', + }, + }, + ]; + + // this call represents an exception list with a value list containing ['2.2.2.2'] + (listClient.getListItemByValues as jest.Mock).mockResolvedValueOnce([ + { ...getListItemResponseMock(), value: '2.2.2.2' }, + ]); + // this call represents an exception list with a value list containing ['4.4.4.4'] + (listClient.getListItemByValues as jest.Mock).mockResolvedValueOnce([ + { ...getListItemResponseMock(), value: '4.4.4.4' }, + ]); + + const res = await filterEventsAgainstList({ + logger: mockLogger, + listClient, + exceptionsList: [exceptionItem], + eventSearchResult: repeatedSearchResultsWithSortId( + 3, + 3, + someGuids.slice(0, 3), + [ + ['1.1.1.1', '1.1.1.1'], + ['1.1.1.1', '2.2.2.2'], + ['2.2.2.2', '3.3.3.3'], + ], + [ + ['1.1.1.1', '2.2.2.2'], + ['2.2.2.2', '3.3.3.3'], + ['3.3.3.3', '4.4.4.4'], + ] + ), + buildRuleMessage, + }); + expect(listClient.getListItemByValues as jest.Mock).toHaveBeenCalledTimes(2); + expect((listClient.getListItemByValues as jest.Mock).mock.calls[0][0].value).toEqual([ + '1.1.1.1', + '2.2.2.2', + '3.3.3.3', + ]); + expect((listClient.getListItemByValues as jest.Mock).mock.calls[1][0].value).toEqual([ + '1.1.1.1', + '2.2.2.2', + '3.3.3.3', + '4.4.4.4', + ]); + expect(res.hits.hits.length).toEqual(2); + + // @ts-expect-error + const sourceIpVals = res.hits.hits.map((item) => item._source.source.ip); + expect([ + ['1.1.1.1', '2.2.2.2'], + ['2.2.2.2', '3.3.3.3'], + ]).toEqual(sourceIpVals); + // @ts-expect-error + const destIpVals = res.hits.hits.map((item) => item._source.destination.ip); + expect([ + ['2.2.2.2', '3.3.3.3'], + ['3.3.3.3', '4.4.4.4'], + ]).toEqual(destIpVals); + }); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filter_events_with_list.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filter_events_with_list.ts index 908d073fbeabd..1c13de16d9b1e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filter_events_with_list.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filter_events_with_list.ts @@ -7,7 +7,6 @@ import { get } from 'lodash/fp'; import { Logger } from 'src/core/server'; import { ListClient } from '../../../../../lists/server'; -import { SignalSearchResponse } from './types'; import { BuildRuleMessage } from './rule_messages'; import { EntryList, @@ -17,16 +16,23 @@ import { } from '../../../../../lists/common/schemas'; import { hasLargeValueList } from '../../../../common/detection_engine/utils'; import { SearchTypes } from '../../../../common/detection_engine/types'; +import { SearchResponse } from '../../types'; -interface FilterEventsAgainstList { - listClient: ListClient; - exceptionsList: ExceptionListItemSchema[]; - logger: Logger; - eventSearchResult: SignalSearchResponse; - buildRuleMessage: BuildRuleMessage; -} +// narrow unioned type to be single +const isStringableType = (val: SearchTypes): val is string | number | boolean => + ['string', 'number', 'boolean'].includes(typeof val); + +const isStringableArray = (val: SearchTypes): val is Array => { + if (!Array.isArray(val)) { + return false; + } + // TS does not allow .every to be called on val as-is, even though every type in the union + // is an array. https://github.com/microsoft/TypeScript/issues/36390 + // @ts-expect-error + return val.every((subVal) => isStringableType(subVal)); +}; -export const createSetToFilterAgainst = async ({ +export const createSetToFilterAgainst = async ({ events, field, listId, @@ -35,7 +41,7 @@ export const createSetToFilterAgainst = async ({ logger, buildRuleMessage, }: { - events: SignalSearchResponse['hits']['hits']; + events: SearchResponse['hits']['hits']; field: string; listId: string; listType: Type; @@ -43,13 +49,14 @@ export const createSetToFilterAgainst = async ({ logger: Logger; buildRuleMessage: BuildRuleMessage; }): Promise> => { - // narrow unioned type to be single - const isStringableType = (val: SearchTypes) => - ['string', 'number', 'boolean'].includes(typeof val); const valuesFromSearchResultField = events.reduce((acc, searchResultItem) => { const valueField = get(field, searchResultItem._source); - if (valueField != null && isStringableType(valueField)) { - acc.add(valueField.toString()); + if (valueField != null) { + if (isStringableType(valueField)) { + acc.add(valueField.toString()); + } else if (isStringableArray(valueField)) { + valueField.forEach((subVal) => acc.add(subVal.toString())); + } } return acc; }, new Set()); @@ -71,13 +78,19 @@ export const createSetToFilterAgainst = async ({ return matchedListItemsSet; }; -export const filterEventsAgainstList = async ({ +export const filterEventsAgainstList = async ({ listClient, exceptionsList, logger, eventSearchResult, buildRuleMessage, -}: FilterEventsAgainstList): Promise => { +}: { + listClient: ListClient; + exceptionsList: ExceptionListItemSchema[]; + logger: Logger; + eventSearchResult: SearchResponse; + buildRuleMessage: BuildRuleMessage; +}): Promise> => { try { if (exceptionsList == null || exceptionsList.length === 0) { logger.debug(buildRuleMessage('about to return original search result')); @@ -108,9 +121,9 @@ export const filterEventsAgainstList = async ({ }); // now that we have all the exception items which are value lists (whether single entry or have multiple entries) - const res = await valueListExceptionItems.reduce>( + const res = await valueListExceptionItems.reduce['hits']['hits']>>( async ( - filteredAccum: Promise, + filteredAccum: Promise['hits']['hits']>, exceptionItem: ExceptionListItemSchema ) => { // 1. acquire the values from the specified fields to check @@ -152,15 +165,23 @@ export const filterEventsAgainstList = async ({ const vals = fieldAndSetTuples.map((tuple) => { const eventItem = get(tuple.field, item._source); if (tuple.operator === 'included') { - // only create a signal if the event is not in the value list + // only create a signal if the field value is not in the value list if (eventItem != null) { - return !tuple.matchedSet.has(eventItem); + if (isStringableType(eventItem)) { + return !tuple.matchedSet.has(eventItem); + } else if (isStringableArray(eventItem)) { + return !eventItem.some((val) => tuple.matchedSet.has(val)); + } } return true; } else if (tuple.operator === 'excluded') { - // only create a signal if the event is in the value list + // only create a signal if the field value is in the value list if (eventItem != null) { - return tuple.matchedSet.has(eventItem); + if (isStringableType(eventItem)) { + return tuple.matchedSet.has(eventItem); + } else if (isStringableArray(eventItem)) { + return eventItem.some((val) => tuple.matchedSet.has(val)); + } } return true; } @@ -175,10 +196,10 @@ export const filterEventsAgainstList = async ({ const toReturn = filteredEvents; return toReturn; }, - Promise.resolve(eventSearchResult.hits.hits) + Promise.resolve['hits']['hits']>(eventSearchResult.hits.hits) ); - const toReturn: SignalSearchResponse = { + const toReturn: SearchResponse = { took: eventSearchResult.took, timed_out: eventSearchResult.timed_out, _shards: eventSearchResult._shards, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/find_ml_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/find_ml_signals.ts index 94b73fce79f0c..ec653f088b523 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/find_ml_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/find_ml_signals.ts @@ -5,6 +5,7 @@ */ import dateMath from '@elastic/datemath'; +import { ExceptionListItemSchema } from '../../../../../lists/common'; import { KibanaRequest, SavedObjectsClientContract } from '../../../../../../../src/core/server'; import { MlPluginSetup } from '../../../../../ml/server'; @@ -18,6 +19,7 @@ export const findMlSignals = async ({ anomalyThreshold, from, to, + exceptionItems, }: { ml: MlPluginSetup; request: KibanaRequest; @@ -26,6 +28,7 @@ export const findMlSignals = async ({ anomalyThreshold: number; from: string; to: string; + exceptionItems: ExceptionListItemSchema[]; }): Promise => { const { mlAnomalySearch } = ml.mlSystemProvider(request, savedObjectsClient); const params = { @@ -33,6 +36,7 @@ export const findMlSignals = async ({ threshold: anomalyThreshold, earliestMs: dateMath.parse(from)?.valueOf() ?? 0, latestMs: dateMath.parse(to)?.valueOf() ?? 0, + exceptionItems, }; return getAnomalies(params, mlAnomalySearch); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts index f0b1825c7cc99..d6bdc14a92b40 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts @@ -66,6 +66,7 @@ import { buildSignalFromEvent, buildSignalGroupFromSequence } from './build_bulk import { createThreatSignals } from './threat_mapping/create_threat_signals'; import { getIndexVersion } from '../routes/index/get_index_version'; import { MIN_EQL_RULE_INDEX_VERSION } from '../routes/index/get_signals_template'; +import { filterEventsAgainstList } from './filter_events_with_list'; export const signalRulesAlertType = ({ logger, @@ -242,9 +243,18 @@ export const signalRulesAlertType = ({ anomalyThreshold, from, to, + exceptionItems: exceptionItems ?? [], + }); + + const filteredAnomalyResults = await filterEventsAgainstList({ + listClient, + exceptionsList: exceptionItems ?? [], + logger, + eventSearchResult: anomalyResults, + buildRuleMessage, }); - const anomalyCount = anomalyResults.hits.hits.length; + const anomalyCount = filteredAnomalyResults.hits.hits.length; if (anomalyCount) { logger.info(buildRuleMessage(`Found ${anomalyCount} signals from ML anomalies.`)); } @@ -257,7 +267,7 @@ export const signalRulesAlertType = ({ } = await bulkCreateMlSignals({ actions, throttle, - someResult: anomalyResults, + someResult: filteredAnomalyResults, ruleParams: params, services, logger, @@ -276,15 +286,16 @@ export const signalRulesAlertType = ({ }); // The legacy ES client does not define failures when it can be present on the structure, hence why I have the & { failures: [] } const shardFailures = - (anomalyResults._shards as typeof anomalyResults._shards & { failures: [] }).failures ?? - []; + (filteredAnomalyResults._shards as typeof filteredAnomalyResults._shards & { + failures: []; + }).failures ?? []; const searchErrors = createErrorsFromShard({ errors: shardFailures, }); result = mergeReturns([ result, createSearchAfterReturnType({ - success: success && anomalyResults._shards.failed === 0, + success: success && filteredAnomalyResults._shards.failed === 0, errors: [...errors, ...searchErrors], createdSignalsCount: createdItemsCount, bulkCreateTimes: bulkCreateDuration ? [bulkCreateDuration] : [], diff --git a/x-pack/plugins/security_solution/server/lib/machine_learning/index.test.ts b/x-pack/plugins/security_solution/server/lib/machine_learning/index.test.ts index 63e3f3487e482..d08b5e649451c 100644 --- a/x-pack/plugins/security_solution/server/lib/machine_learning/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/machine_learning/index.test.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { getExceptionListItemSchemaMock } from '../../../../lists/common/schemas/response/exception_list_item_schema.mock'; import { getAnomalies, AnomaliesSearchParams } from '.'; const getFiltersFromMock = (mock: jest.Mock) => { @@ -23,6 +24,7 @@ describe('getAnomalies', () => { threshold: 5, earliestMs: 1588517231429, latestMs: 1588617231429, + exceptionItems: [getExceptionListItemSchemaMock(), getExceptionListItemSchemaMock()], }; }); diff --git a/x-pack/plugins/security_solution/server/lib/machine_learning/index.ts b/x-pack/plugins/security_solution/server/lib/machine_learning/index.ts index 34e004d817fe7..ec801f6c49ae7 100644 --- a/x-pack/plugins/security_solution/server/lib/machine_learning/index.ts +++ b/x-pack/plugins/security_solution/server/lib/machine_learning/index.ts @@ -4,10 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SearchResponse } from 'elasticsearch'; import { RequestParams } from '@elastic/elasticsearch'; +import { ExceptionListItemSchema } from '../../../../lists/common'; +import { buildExceptionFilter } from '../../../common/detection_engine/get_query_filter'; import { AnomalyRecordDoc as Anomaly } from '../../../../ml/server'; +import { SearchResponse } from '../types'; export { Anomaly }; export type AnomalyResults = SearchResponse; @@ -21,6 +23,7 @@ export interface AnomaliesSearchParams { threshold: number; earliestMs: number; latestMs: number; + exceptionItems: ExceptionListItemSchema[]; maxRecords?: number; } @@ -49,6 +52,17 @@ export const getAnomalies = async ( }, }, ], + must_not: buildExceptionFilter({ + lists: params.exceptionItems, + config: { + allowLeadingWildcards: true, + queryStringOptions: { analyze_wildcard: true }, + ignoreFilterIfFieldNotInIndex: false, + dateFormatTZ: 'Zulu', + }, + excludeExceptions: true, + chunkSize: 1024, + })?.query, }, }, sort: [{ record_score: { order: 'desc' } }], From e1944342af1a4f9834092e3ef31ab9cf832e5274 Mon Sep 17 00:00:00 2001 From: Dan Panzarella Date: Wed, 2 Dec 2020 17:30:44 -0500 Subject: [PATCH 22/40] [Security Solution] Keep Endpoint policies up to date with license changes (#83992) --- .../endpoint/lib/policy/license_watch.test.ts | 133 ++++++++++++++++++ .../endpoint/lib/policy/license_watch.ts | 116 +++++++++++++++ .../security_solution/server/plugin.ts | 10 +- 3 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts create mode 100644 x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.ts diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts b/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts new file mode 100644 index 0000000000000..5773b88fa2bea --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts @@ -0,0 +1,133 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Subject } from 'rxjs'; +import { loggingSystemMock, savedObjectsServiceMock } from 'src/core/server/mocks'; +import { LicenseService } from '../../../../common/license/license'; +import { createPackagePolicyServiceMock } from '../../../../../fleet/server/mocks'; +import { PolicyWatcher } from './license_watch'; +import { ILicense } from '../../../../../licensing/common/types'; +import { licenseMock } from '../../../../../licensing/common/licensing.mock'; +import { PackagePolicyServiceInterface } from '../../../../../fleet/server'; +import { PackagePolicy } from '../../../../../fleet/common'; +import { createPackagePolicyMock } from '../../../../../fleet/common/mocks'; +import { factory } from '../../../../common/endpoint/models/policy_config'; +import { PolicyConfig } from '../../../../common/endpoint/types'; + +const MockPPWithEndpointPolicy = (cb?: (p: PolicyConfig) => PolicyConfig): PackagePolicy => { + const packagePolicy = createPackagePolicyMock(); + if (!cb) { + // eslint-disable-next-line no-param-reassign + cb = (p) => p; + } + const policyConfig = cb(factory()); + packagePolicy.inputs[0].config = { policy: { value: policyConfig } }; + return packagePolicy; +}; + +describe('Policy-Changing license watcher', () => { + const logger = loggingSystemMock.create().get('license_watch.test'); + const soStartMock = savedObjectsServiceMock.createStartContract(); + let packagePolicySvcMock: jest.Mocked; + + const Platinum = licenseMock.createLicense({ license: { type: 'platinum', mode: 'platinum' } }); + const Gold = licenseMock.createLicense({ license: { type: 'gold', mode: 'gold' } }); + const Basic = licenseMock.createLicense({ license: { type: 'basic', mode: 'basic' } }); + + beforeEach(() => { + packagePolicySvcMock = createPackagePolicyServiceMock(); + }); + + it('is activated on license changes', () => { + // mock a license-changing service to test reactivity + const licenseEmitter: Subject = new Subject(); + const licenseService = new LicenseService(); + const pw = new PolicyWatcher(packagePolicySvcMock, soStartMock, logger); + + // swap out watch function, just to ensure it gets called when a license change happens + const mockWatch = jest.fn(); + pw.watch = mockWatch; + + // licenseService is watching our subject for incoming licenses + licenseService.start(licenseEmitter); + pw.start(licenseService); // and the PolicyWatcher under test, uses that to subscribe as well + + // Enqueue a license change! + licenseEmitter.next(Platinum); + + // policywatcher should have triggered + expect(mockWatch.mock.calls.length).toBe(1); + + pw.stop(); + licenseService.stop(); + licenseEmitter.complete(); + }); + + it('pages through all endpoint policies', async () => { + const TOTAL = 247; + + // set up the mocked package policy service to return and do what we want + packagePolicySvcMock.list + .mockResolvedValueOnce({ + items: Array.from({ length: 100 }, () => MockPPWithEndpointPolicy()), + total: TOTAL, + page: 1, + perPage: 100, + }) + .mockResolvedValueOnce({ + items: Array.from({ length: 100 }, () => MockPPWithEndpointPolicy()), + total: TOTAL, + page: 2, + perPage: 100, + }) + .mockResolvedValueOnce({ + items: Array.from({ length: TOTAL - 200 }, () => MockPPWithEndpointPolicy()), + total: TOTAL, + page: 3, + perPage: 100, + }); + + const pw = new PolicyWatcher(packagePolicySvcMock, soStartMock, logger); + await pw.watch(Gold); // just manually trigger with a given license + + expect(packagePolicySvcMock.list.mock.calls.length).toBe(3); // should have asked for 3 pages of resuts + + // Assert: on the first call to packagePolicy.list, we asked for page 1 + expect(packagePolicySvcMock.list.mock.calls[0][1].page).toBe(1); + expect(packagePolicySvcMock.list.mock.calls[1][1].page).toBe(2); // second call, asked for page 2 + expect(packagePolicySvcMock.list.mock.calls[2][1].page).toBe(3); // etc + }); + + it('alters no-longer-licensed features', async () => { + const CustomMessage = 'Custom string'; + + // mock a Policy with a higher-tiered feature enabled + packagePolicySvcMock.list.mockResolvedValueOnce({ + items: [ + MockPPWithEndpointPolicy( + (pc: PolicyConfig): PolicyConfig => { + pc.windows.popup.malware.message = CustomMessage; + return pc; + } + ), + ], + total: 1, + page: 1, + perPage: 100, + }); + + const pw = new PolicyWatcher(packagePolicySvcMock, soStartMock, logger); + + // emulate a license change below paid tier + await pw.watch(Basic); + + expect(packagePolicySvcMock.update).toHaveBeenCalled(); + expect( + packagePolicySvcMock.update.mock.calls[0][2].inputs[0].config!.policy.value.windows.popup + .malware.message + ).not.toEqual(CustomMessage); + }); +}); diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.ts b/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.ts new file mode 100644 index 0000000000000..cae3b9f33850a --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.ts @@ -0,0 +1,116 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Subscription } from 'rxjs'; + +import { + KibanaRequest, + Logger, + SavedObjectsClientContract, + SavedObjectsServiceStart, +} from 'src/core/server'; +import { PackagePolicy, PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../../../fleet/common'; +import { PackagePolicyServiceInterface } from '../../../../../fleet/server'; +import { ILicense } from '../../../../../licensing/common/types'; +import { + isEndpointPolicyValidForLicense, + unsetPolicyFeaturesAboveLicenseLevel, +} from '../../../../common/license/policy_config'; +import { isAtLeast, LicenseService } from '../../../../common/license/license'; + +export class PolicyWatcher { + private logger: Logger; + private soClient: SavedObjectsClientContract; + private policyService: PackagePolicyServiceInterface; + private subscription: Subscription | undefined; + constructor( + policyService: PackagePolicyServiceInterface, + soStart: SavedObjectsServiceStart, + logger: Logger + ) { + this.policyService = policyService; + this.soClient = this.makeInternalSOClient(soStart); + this.logger = logger; + } + + /** + * The policy watcher is not called as part of a HTTP request chain, where the + * request-scoped SOClient could be passed down. It is called via license observable + * changes. We are acting as the 'system' in response to license changes, so we are + * intentionally using the system user here. Be very aware of what you are using this + * client to do + */ + private makeInternalSOClient(soStart: SavedObjectsServiceStart): SavedObjectsClientContract { + const fakeRequest = ({ + headers: {}, + getBasePath: () => '', + path: '/', + route: { settings: {} }, + url: { href: {} }, + raw: { req: { url: '/' } }, + } as unknown) as KibanaRequest; + return soStart.getScopedClient(fakeRequest, { excludedWrappers: ['security'] }); + } + + public start(licenseService: LicenseService) { + this.subscription = licenseService.getLicenseInformation$()?.subscribe(this.watch.bind(this)); + } + + public stop() { + if (this.subscription) { + this.subscription.unsubscribe(); + } + } + + public async watch(license: ILicense) { + if (isAtLeast(license, 'platinum')) { + return; + } + + let page = 1; + let response: { + items: PackagePolicy[]; + total: number; + page: number; + perPage: number; + }; + do { + try { + response = await this.policyService.list(this.soClient, { + page: page++, + perPage: 100, + kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: endpoint`, + }); + } catch (e) { + this.logger.warn( + `Unable to verify endpoint policies in line with license change: failed to fetch package policies: ${e.message}` + ); + return; + } + response.items.forEach(async (policy) => { + const policyConfig = policy.inputs[0].config?.policy.value; + if (!isEndpointPolicyValidForLicense(policyConfig, license)) { + policy.inputs[0].config!.policy.value = unsetPolicyFeaturesAboveLicenseLevel( + policyConfig, + license + ); + try { + await this.policyService.update(this.soClient, policy.id, policy); + } catch (e) { + // try again for transient issues + try { + await this.policyService.update(this.soClient, policy.id, policy); + } catch (ee) { + this.logger.warn( + `Unable to remove platinum features from policy ${policy.id}: ${ee.message}` + ); + } + } + } + }); + } while (response.page * response.perPage < response.total); + } +} diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 088af40a84ae0..10e817bea0282 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -75,6 +75,7 @@ import { TelemetryPluginSetup, } from '../../../../src/plugins/telemetry/server'; import { licenseService } from './lib/license/license'; +import { PolicyWatcher } from './endpoint/lib/policy/license_watch'; export interface SetupPlugins { alerts: AlertingSetup; @@ -127,6 +128,7 @@ export class Plugin implements IPlugin; + private policyWatcher?: PolicyWatcher; private manifestTask: ManifestTask | undefined; private exceptionsCache: LRU; @@ -370,7 +372,12 @@ export class Plugin implements IPlugin Date: Wed, 2 Dec 2020 23:40:06 +0100 Subject: [PATCH 23/40] Upgrade Node.js to version 14 (#83425) --- .ci/Dockerfile | 2 +- .node-version | 2 +- .nvmrc | 2 +- package.json | 12 +-- .../kbn-legacy-logging/src/setup_logging.ts | 2 +- packages/kbn-pm/.babelrc | 3 +- .../fake_mocha_types.d.ts | 2 +- .../src/streams/reduce_stream.test.ts | 4 +- src/cli/cluster/cluster.mock.ts | 2 +- src/cli/repl/__snapshots__/repl.test.js.snap | 19 +++-- src/core/public/utils/crypto/sha256.ts | 2 +- .../client/configure_client.test.ts | 2 +- .../server/metrics/collectors/process.test.ts | 1 + .../build/tasks/patch_native_modules_task.ts | 18 ++--- .../public/state_management/url/format.ts | 6 ++ .../state_management/url/kbn_url_storage.ts | 6 ++ .../authentication/login/login_page.tsx | 1 + .../public/management/common/routing.ts | 5 +- .../pages/endpoint_hosts/store/selectors.ts | 2 +- .../lib/timeline/routes/utils/common.ts | 2 +- .../dashboard/reporting/lib/compare_pngs.ts | 17 ++-- yarn.lock | 79 ++++++++++++------- 22 files changed, 116 insertions(+), 75 deletions(-) diff --git a/.ci/Dockerfile b/.ci/Dockerfile index b2254c8fb1e05..ec7befe05f0d4 100644 --- a/.ci/Dockerfile +++ b/.ci/Dockerfile @@ -1,7 +1,7 @@ # NOTE: This Dockerfile is ONLY used to run certain tasks in CI. It is not used to run Kibana or as a distributable. # If you're looking for the Kibana Docker image distributable, please see: src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts -ARG NODE_VERSION=12.19.1 +ARG NODE_VERSION=14.15.1 FROM node:${NODE_VERSION} AS base diff --git a/.node-version b/.node-version index e9f788b12771f..2f5ee741e0d77 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -12.19.1 +14.15.1 diff --git a/.nvmrc b/.nvmrc index e9f788b12771f..2f5ee741e0d77 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -12.19.1 +14.15.1 diff --git a/package.json b/package.json index d5c1f247d87d7..77368f5caa7ee 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "**/@types/hapi__boom": "^7.4.1", "**/@types/hapi__hapi": "^18.2.6", "**/@types/hapi__mimos": "4.1.0", - "**/@types/node": "12.19.4", + "**/@types/node": "14.14.7", "**/cross-fetch/node-fetch": "^2.6.1", "**/deepmerge": "^4.2.2", "**/fast-deep-equal": "^3.1.1", @@ -98,7 +98,7 @@ "**/typescript": "4.1.2" }, "engines": { - "node": "12.19.1", + "node": "14.15.1", "yarn": "^1.21.1" }, "dependencies": { @@ -109,7 +109,7 @@ "@elastic/ems-client": "7.11.0", "@elastic/eui": "30.2.0", "@elastic/filesaver": "1.1.2", - "@elastic/good": "8.1.1-kibana2", + "@elastic/good": "^9.0.1-kibana3", "@elastic/node-crypto": "1.2.1", "@elastic/numeral": "^2.5.0", "@elastic/request-crypto": "1.1.4", @@ -496,7 +496,7 @@ "@types/mustache": "^0.8.31", "@types/ncp": "^2.0.1", "@types/nock": "^10.0.3", - "@types/node": "12.19.4", + "@types/node": "14.14.7", "@types/node-fetch": "^2.5.7", "@types/node-forge": "^0.9.5", "@types/nodemailer": "^6.4.0", @@ -722,7 +722,7 @@ "less": "npm:@elastic/less@2.7.3-kibana", "license-checker": "^16.0.0", "listr": "^0.14.1", - "lmdb-store": "^0.8.15", + "lmdb-store": "^0.9.0", "load-grunt-config": "^3.0.1", "loader-utils": "^1.2.3", "log-symbols": "^2.2.0", @@ -805,7 +805,7 @@ "sass-resources-loader": "^2.0.1", "selenium-webdriver": "^4.0.0-alpha.7", "serve-static": "1.14.1", - "shelljs": "^0.8.3", + "shelljs": "^0.8.4", "simple-git": "1.116.0", "sinon": "^7.4.2", "spawn-sync": "^1.0.15", diff --git a/packages/kbn-legacy-logging/src/setup_logging.ts b/packages/kbn-legacy-logging/src/setup_logging.ts index 3b8b4b167c63d..153e7a0f207c1 100644 --- a/packages/kbn-legacy-logging/src/setup_logging.ts +++ b/packages/kbn-legacy-logging/src/setup_logging.ts @@ -18,7 +18,7 @@ */ // @ts-expect-error missing typedef -import good from '@elastic/good'; +import { plugin as good } from '@elastic/good'; import { Server } from '@hapi/hapi'; import { LegacyLoggingConfig } from './schema'; import { getLoggingConfiguration } from './get_logging_config'; diff --git a/packages/kbn-pm/.babelrc b/packages/kbn-pm/.babelrc index 1ca768097a7ee..9ea6ecafe7287 100644 --- a/packages/kbn-pm/.babelrc +++ b/packages/kbn-pm/.babelrc @@ -9,6 +9,7 @@ ], "plugins": [ "@babel/proposal-class-properties", - "@babel/proposal-object-rest-spread" + "@babel/proposal-object-rest-spread", + "@babel/proposal-optional-chaining" ] } diff --git a/packages/kbn-test/src/functional_test_runner/fake_mocha_types.d.ts b/packages/kbn-test/src/functional_test_runner/fake_mocha_types.d.ts index 35b4b85e4d22a..a1e5b2a363a9d 100644 --- a/packages/kbn-test/src/functional_test_runner/fake_mocha_types.d.ts +++ b/packages/kbn-test/src/functional_test_runner/fake_mocha_types.d.ts @@ -23,7 +23,7 @@ * tries to mock out simple versions of the Mocha types */ -import EventEmitter from 'events'; +import { EventEmitter } from 'events'; export interface Suite { suites: Suite[]; diff --git a/packages/kbn-utils/src/streams/reduce_stream.test.ts b/packages/kbn-utils/src/streams/reduce_stream.test.ts index e4a7dc1cef491..7d823bb8fe113 100644 --- a/packages/kbn-utils/src/streams/reduce_stream.test.ts +++ b/packages/kbn-utils/src/streams/reduce_stream.test.ts @@ -70,7 +70,7 @@ describe('reduceStream', () => { const errorStub = jest.fn(); reduce$.on('data', dataStub); reduce$.on('error', errorStub); - const endEvent = promiseFromEvent('end', reduce$); + const closeEvent = promiseFromEvent('close', reduce$); reduce$.write(1); reduce$.write(2); @@ -79,7 +79,7 @@ describe('reduceStream', () => { reduce$.write(1000); reduce$.end(); - await endEvent; + await closeEvent; expect(reducer).toHaveBeenCalledTimes(3); expect(dataStub).toHaveBeenCalledTimes(0); expect(errorStub).toHaveBeenCalledTimes(1); diff --git a/src/cli/cluster/cluster.mock.ts b/src/cli/cluster/cluster.mock.ts index 332f8aad53ba1..85d16a79a467c 100644 --- a/src/cli/cluster/cluster.mock.ts +++ b/src/cli/cluster/cluster.mock.ts @@ -19,7 +19,7 @@ /* eslint-env jest */ // eslint-disable-next-line max-classes-per-file -import EventEmitter from 'events'; +import { EventEmitter } from 'events'; import { assign, random } from 'lodash'; import { delay } from 'bluebird'; diff --git a/src/cli/repl/__snapshots__/repl.test.js.snap b/src/cli/repl/__snapshots__/repl.test.js.snap index c7751b5797f49..804898284491d 100644 --- a/src/cli/repl/__snapshots__/repl.test.js.snap +++ b/src/cli/repl/__snapshots__/repl.test.js.snap @@ -1,17 +1,22 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`repl it allows print depth to be specified 1`] = `"{ '0': { '1': { '2': [Object] } }, whoops: [Circular] }"`; +exports[`repl it allows print depth to be specified 1`] = ` +" { + '0': { '1': { '2': [Object] } }, + whoops: [Circular *1] +}" +`; exports[`repl it colorizes raw values 1`] = `"{ meaning: 42 }"`; exports[`repl it handles deep and recursive objects 1`] = ` -"{ +" { '0': { '1': { '2': { '3': { '4': { '5': [Object] } } } } }, - whoops: [Circular] + whoops: [Circular *1] }" `; @@ -51,13 +56,13 @@ Array [ Array [ "Promise Rejected: ", - "{ + " { '0': { '1': { '2': { '3': { '4': { '5': [Object] } } } } }, - whoops: [Circular] + whoops: [Circular *1] }", ], ] @@ -71,13 +76,13 @@ Array [ Array [ "Promise Resolved: ", - "{ + " { '0': { '1': { '2': { '3': { '4': { '5': [Object] } } } } }, - whoops: [Circular] + whoops: [Circular *1] }", ], ] diff --git a/src/core/public/utils/crypto/sha256.ts b/src/core/public/utils/crypto/sha256.ts index 13e0d405a706b..add93cb75b92a 100644 --- a/src/core/public/utils/crypto/sha256.ts +++ b/src/core/public/utils/crypto/sha256.ts @@ -200,7 +200,7 @@ export class Sha256 { return this; } - digest(encoding: string): string { + digest(encoding: BufferEncoding): string { // Suppose the length of the message M, in bits, is l const l = this._len * 8; diff --git a/src/core/server/elasticsearch/client/configure_client.test.ts b/src/core/server/elasticsearch/client/configure_client.test.ts index 614ec112e8f0b..22cb7275b6a23 100644 --- a/src/core/server/elasticsearch/client/configure_client.test.ts +++ b/src/core/server/elasticsearch/client/configure_client.test.ts @@ -24,7 +24,7 @@ import { TransportRequestParams, RequestBody } from '@elastic/elasticsearch/lib/ import { parseClientOptionsMock, ClientMock } from './configure_client.test.mocks'; import { loggingSystemMock } from '../../logging/logging_system.mock'; -import EventEmitter from 'events'; +import { EventEmitter } from 'events'; import type { ElasticsearchClientConfig } from './client_config'; import { configureClient } from './configure_client'; diff --git a/src/core/server/metrics/collectors/process.test.ts b/src/core/server/metrics/collectors/process.test.ts index a437d799371f1..0ce1b9e8e350e 100644 --- a/src/core/server/metrics/collectors/process.test.ts +++ b/src/core/server/metrics/collectors/process.test.ts @@ -62,6 +62,7 @@ describe('ProcessMetricsCollector', () => { heapTotal, heapUsed, external: 0, + arrayBuffers: 0, })); jest.spyOn(v8, 'getHeapStatistics').mockImplementation( diff --git a/src/dev/build/tasks/patch_native_modules_task.ts b/src/dev/build/tasks/patch_native_modules_task.ts index b6eda2dbfd560..0819123138d0f 100644 --- a/src/dev/build/tasks/patch_native_modules_task.ts +++ b/src/dev/build/tasks/patch_native_modules_task.ts @@ -47,12 +47,12 @@ const packages: Package[] = [ extractMethod: 'gunzip', archives: { 'darwin-x64': { - url: 'https://github.com/uhop/node-re2/releases/download/1.15.4/darwin-x64-72.gz', - sha256: '983106049bb86e21b7f823144b2b83e3f1408217401879b3cde0312c803512c9', + url: 'https://github.com/uhop/node-re2/releases/download/1.15.4/darwin-x64-83.gz', + sha256: 'b45cd8296fd6eb2a091399c20111af43093ba30c99ed9e5d969278f5ff69ba8f', }, 'linux-x64': { - url: 'https://github.com/uhop/node-re2/releases/download/1.15.4/linux-x64-72.gz', - sha256: '8b6692037f7b0df24dabc9c9b039038d1c3a3110f62121616b406c482169710a', + url: 'https://github.com/uhop/node-re2/releases/download/1.15.4/linux-x64-83.gz', + sha256: '1bbc3f90f0ba105772b37c04e3a718f69544b4df01dda00435c2b8e50b2ad0d9', }, // ARM build is currently done manually as Github Actions used in upstream project @@ -62,16 +62,16 @@ const packages: Package[] = [ // * checkout the node-re2 project, // * install Node using the same minor used by Kibana // * npm install, which will also create a build - // * gzip -c build/Release/re2.node > linux-arm64-72.gz + // * gzip -c build/Release/re2.node > linux-arm64-83.gz // * upload to kibana-ci-proxy-cache bucket 'linux-arm64': { url: - 'https://storage.googleapis.com/kibana-ci-proxy-cache/node-re2/uhop/node-re2/releases/download/1.15.4/linux-arm64-72.gz', - sha256: '5942353ec9cf46a39199818d474f7af137cfbb1bc5727047fe22f31f36602a7e', + 'https://storage.googleapis.com/kibana-ci-proxy-cache/node-re2/uhop/node-re2/releases/download/1.15.4/linux-arm64-83.gz', + sha256: '4eb524ca9a79dea9c07342e487fbe91591166fdbc022ae987104840df948a4e9', }, 'win32-x64': { - url: 'https://github.com/uhop/node-re2/releases/download/1.15.4/win32-x64-72.gz', - sha256: '0a6991e693577160c3e9a3f196bd2518368c52d920af331a1a183313e0175604', + url: 'https://github.com/uhop/node-re2/releases/download/1.15.4/win32-x64-83.gz', + sha256: 'efe939d3cda1d64ee3ee3e60a20613b95166d55632e702c670763ea7e69fca06', }, }, }, diff --git a/src/plugins/kibana_utils/public/state_management/url/format.ts b/src/plugins/kibana_utils/public/state_management/url/format.ts index 4497e509bc86b..4a3d725de7e47 100644 --- a/src/plugins/kibana_utils/public/state_management/url/format.ts +++ b/src/plugins/kibana_utils/public/state_management/url/format.ts @@ -27,6 +27,9 @@ export function replaceUrlQuery( queryReplacer: (query: ParsedQuery) => ParsedQuery ) { const url = parseUrl(rawUrl); + // @ts-expect-error `queryReplacer` expects key/value pairs with values of type `string | string[] | null`, + // however `@types/node` says that `url.query` has values of type `string | string[] | undefined`. + // After investigating this, it seems that no matter what the values will be of type `string | string[]` const newQuery = queryReplacer(url.query || {}); const searchQueryString = stringify(urlUtils.encodeQuery(newQuery), { sort: false, @@ -45,6 +48,9 @@ export function replaceUrlHashQuery( ) { const url = parseUrl(rawUrl); const hash = parseUrlHash(rawUrl); + // @ts-expect-error `queryReplacer` expects key/value pairs with values of type `string | string[] | null`, + // however `@types/node` says that `url.query` has values of type `string | string[] | undefined`. + // After investigating this, it seems that no matter what the values will be of type `string | string[]` const newQuery = queryReplacer(hash?.query || {}); const searchQueryString = stringify(urlUtils.encodeQuery(newQuery), { sort: false, diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts index cb3c9470c7abd..8ec7ad00d7926 100644 --- a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts +++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts @@ -252,10 +252,16 @@ export function getRelativeToHistoryPath(absoluteUrl: string, history: History): return formatUrl({ pathname: stripBasename(parsedUrl.pathname ?? null), + // @ts-expect-error `urlUtils.encodeQuery` expects key/value pairs with values of type `string | string[] | null`, + // however `@types/node` says that `url.query` has values of type `string | string[] | undefined`. + // After investigating this, it seems that no matter what the values will be of type `string | string[]` search: stringify(urlUtils.encodeQuery(parsedUrl.query), { sort: false, encode: false }), hash: parsedHash ? formatUrl({ pathname: parsedHash.pathname, + // @ts-expect-error `urlUtils.encodeQuery` expects key/value pairs with values of type `string | string[] | null`, + // however `@types/node` says that `url.query` has values of type `string | string[] | undefined`. + // After investigating this, it seems that no matter what the values will be of type `string | string[]` search: stringify(urlUtils.encodeQuery(parsedHash.query), { sort: false, encode: false }), }) : parsedUrl.hash, diff --git a/x-pack/plugins/security/public/authentication/login/login_page.tsx b/x-pack/plugins/security/public/authentication/login/login_page.tsx index 35703212762fd..3eff6edef33bc 100644 --- a/x-pack/plugins/security/public/authentication/login/login_page.tsx +++ b/x-pack/plugins/security/public/authentication/login/login_page.tsx @@ -222,6 +222,7 @@ export class LoginPage extends Component { http={this.props.http} notifications={this.props.notifications} selector={selector} + // @ts-expect-error Map.get is ok with getting `undefined` infoMessage={infoMessageMap.get(query[LOGOUT_REASON_QUERY_STRING_PARAMETER]?.toString())} loginAssistanceMessage={this.props.loginAssistanceMessage} loginHelp={loginHelp} diff --git a/x-pack/plugins/security_solution/public/management/common/routing.ts b/x-pack/plugins/security_solution/public/management/common/routing.ts index c2c82639bf7d5..11caab837a766 100644 --- a/x-pack/plugins/security_solution/public/management/common/routing.ts +++ b/x-pack/plugins/security_solution/public/management/common/routing.ts @@ -118,7 +118,10 @@ const normalizeTrustedAppsPageLocation = ( * @param query * @param key */ -export const extractFirstParamValue = (query: querystring.ParsedUrlQuery, key: string): string => { +export const extractFirstParamValue = ( + query: querystring.ParsedUrlQuery, + key: string +): string | undefined => { const value = query[key]; return Array.isArray(value) ? value[value.length - 1] : value; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts index 1901f3589104a..05c3ac0faea69 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts @@ -186,7 +186,7 @@ export const uiQueryParams: ( typeof query[key] === 'string' ? (query[key] as string) : Array.isArray(query[key]) - ? (query[key][query[key].length - 1] as string) + ? (query[key] as string[])[(query[key] as string[]).length - 1] : undefined; if (value !== undefined) { diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/common.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/common.ts index 488da5025531d..c230e36e4c896 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/common.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/common.ts @@ -39,7 +39,7 @@ export const getReadables = (dataPath: string): Promise => const readable = fs.createReadStream(dataPath, { encoding: 'utf-8' }); readable.on('data', (stream) => { - contents.push(stream); + contents.push(stream as string); }); readable.on('end', () => { diff --git a/x-pack/test/functional/apps/dashboard/reporting/lib/compare_pngs.ts b/x-pack/test/functional/apps/dashboard/reporting/lib/compare_pngs.ts index b2eb645c8372c..b4cd9c361778f 100644 --- a/x-pack/test/functional/apps/dashboard/reporting/lib/compare_pngs.ts +++ b/x-pack/test/functional/apps/dashboard/reporting/lib/compare_pngs.ts @@ -4,13 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { promisify } from 'bluebird'; -import fs from 'fs'; +import { promises as fs } from 'fs'; import path from 'path'; import { comparePngs } from '../../../../../../../test/functional/services/lib/compare_pngs'; -const mkdirAsync = promisify(fs.mkdir); - export async function checkIfPngsMatch( actualpngPath: string, baselinepngPath: string, @@ -23,8 +20,8 @@ export async function checkIfPngsMatch( const sessionDirectoryPath = path.resolve(screenshotsDirectory, 'session'); const failureDirectoryPath = path.resolve(screenshotsDirectory, 'failure'); - await mkdirAsync(sessionDirectoryPath, { recursive: true }); - await mkdirAsync(failureDirectoryPath, { recursive: true }); + await fs.mkdir(sessionDirectoryPath, { recursive: true }); + await fs.mkdir(failureDirectoryPath, { recursive: true }); const actualpngFileName = path.basename(actualpngPath, '.png'); const baselinepngFileName = path.basename(baselinepngPath, '.png'); @@ -39,14 +36,14 @@ export async function checkIfPngsMatch( // don't want to start causing failures for other devs working on OS's which are lacking snapshots. We have // mac and linux covered which is better than nothing for now. try { - log.debug(`writeFileSync: ${baselineCopyPath}`); - fs.writeFileSync(baselineCopyPath, fs.readFileSync(baselinepngPath)); + log.debug(`writeFile: ${baselineCopyPath}`); + await fs.writeFile(baselineCopyPath, await fs.readFile(baselinepngPath)); } catch (error) { log.error(`No baseline png found at ${baselinepngPath}`); return 0; } - log.debug(`writeFileSync: ${actualCopyPath}`); - fs.writeFileSync(actualCopyPath, fs.readFileSync(actualpngPath)); + log.debug(`writeFile: ${actualCopyPath}`); + await fs.writeFile(actualCopyPath, await fs.readFile(actualpngPath)); let diffTotal = 0; diff --git a/yarn.lock b/yarn.lock index 28f032ef7122f..af1a1493bc36a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1489,15 +1489,14 @@ async-retry "^1.2.3" strip-ansi "^5.2.0" -"@elastic/good@8.1.1-kibana2": - version "8.1.1-kibana2" - resolved "https://registry.yarnpkg.com/@elastic/good/-/good-8.1.1-kibana2.tgz#3ba7413da9fae4c67f128f3e9b1dc28f24523c7a" - integrity sha512-2AYmQMBjmh2896FePnnGr9nwoqRxZ6bTjregDRI0CB9r4sIpIzG6J7oMa0GztdDMlrk5CslX1g9SN5EihddPlw== +"@elastic/good@^9.0.1-kibana3": + version "9.0.1-kibana3" + resolved "https://registry.yarnpkg.com/@elastic/good/-/good-9.0.1-kibana3.tgz#a70c2b30cbb4f44d1cf4a464562e0680322eac9b" + integrity sha512-UtPKr0TmlkL1abJfO7eEVUTqXWzLKjMkz+65FvxU/Ub9kMAr4No8wHLRfDHFzBkWoDIbDWygwld011WzUnea1Q== dependencies: - hoek "5.x.x" - joi "13.x.x" - oppsy "2.x.x" - pumpify "1.3.x" + "@hapi/hoek" "9.x.x" + "@hapi/oppsy" "3.x.x" + "@hapi/validate" "1.x.x" "@elastic/makelogs@^6.0.0": version "6.0.0" @@ -1847,7 +1846,7 @@ resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.1.tgz#fde96064ca446dec8c55a8c2f130957b070c6e06" integrity sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow== -"@hapi/hoek@^9.0.0": +"@hapi/hoek@9.x.x", "@hapi/hoek@^9.0.0": version "9.1.0" resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.1.0.tgz#6c9eafc78c1529248f8f4d92b0799a712b6052c6" integrity sha512-i9YbZPN3QgfighY/1X1Pu118VUz2Fmmhd6b2n0/O8YVgGGfw0FbUYoA97k7FkpGJ+pLCFEDLUmAPPV4D1kpeFw== @@ -1912,6 +1911,13 @@ "@hapi/hoek" "8.x.x" "@hapi/vise" "3.x.x" +"@hapi/oppsy@3.x.x": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@hapi/oppsy/-/oppsy-3.0.0.tgz#1ae397e200e86d0aa41055f103238ed8652947ca" + integrity sha512-0kfUEAqIi21GzFVK2snMO07znMEBiXb+/pOx1dmgOO9TuvFstcfmHU5i56aDfiFP2DM5WzQCU2UWc2gK1lMDhQ== + dependencies: + "@hapi/hoek" "9.x.x" + "@hapi/pez@^4.1.2": version "4.1.2" resolved "https://registry.yarnpkg.com/@hapi/pez/-/pez-4.1.2.tgz#14984d0c31fed348f10c962968a21d9761f55503" @@ -2002,6 +2008,14 @@ dependencies: "@hapi/hoek" "^9.0.0" +"@hapi/validate@1.x.x": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@hapi/validate/-/validate-1.1.3.tgz#f750a07283929e09b51aa16be34affb44e1931ad" + integrity sha512-/XMR0N0wjw0Twzq2pQOzPBZlDzkekGcoCtzO314BpIEsbXdYGthQUbxgkGDf4nhk1+IPDAsXqWjMohRQYO06UA== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hapi/topo" "^5.0.0" + "@hapi/vise@3.x.x": version "3.1.1" resolved "https://registry.yarnpkg.com/@hapi/vise/-/vise-3.1.1.tgz#dfc88f2ac90682f48bdc1b3f9b8f1eab4eabe0c8" @@ -5262,10 +5276,10 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@12.19.4", "@types/node@8.10.54", "@types/node@>= 8", "@types/node@>=8.9.0", "@types/node@^12.0.2": - version "12.19.4" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.4.tgz#cdfbb62e26c7435ed9aab9c941393cc3598e9b46" - integrity sha512-o3oj1bETk8kBwzz1WlO6JWL/AfAA3Vm6J1B3C9CsdxHYp7XgPiH7OEXPUbZTndHlRaIElrANkQfe6ZmfJb3H2w== +"@types/node@*", "@types/node@14.14.7", "@types/node@8.10.54", "@types/node@>= 8", "@types/node@>=8.9.0", "@types/node@^12.0.2": + version "14.14.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.7.tgz#8ea1e8f8eae2430cf440564b98c6dfce1ec5945d" + integrity sha512-Zw1vhUSQZYw+7u5dAwNbIA9TuTotpzY/OF7sJM9FqPOF3SPjKnxrjoTktXDZgUjybf4cWVBP7O8wvKdSaGHweg== "@types/nodemailer@^6.4.0": version "6.4.0" @@ -18818,14 +18832,14 @@ lmdb-store-0.9@0.7.3: node-gyp-build "^4.2.3" weak-lru-cache "^0.3.9" -lmdb-store@^0.8.15: - version "0.8.15" - resolved "https://registry.yarnpkg.com/lmdb-store/-/lmdb-store-0.8.15.tgz#4efb0341c2df505dd6f3a7f26f834f0a142a80a2" - integrity sha512-4Q0WZh2FmcJC6esZRUWMfkCmNiz0WU9cOgrxt97ZMTnVfHyOdZhtrt0oOF5EQPfetxxJf/BorKY28aX92R6G6g== +lmdb-store@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/lmdb-store/-/lmdb-store-0.9.0.tgz#9a07735baaabcb8a46ee08c58ce1d578b69bdc12" + integrity sha512-5yxZ/s2J4w5mq3II5w2i4EiAAT+RvGZ3dtiWPYQDV/F8BpwqZOi7QmHdwawf15stvXv9P92Rm7t2WPbjOV9Xkg== dependencies: fs-extra "^9.0.1" lmdb-store-0.9 "0.7.3" - msgpackr "^0.5.4" + msgpackr "^0.6.0" nan "^2.14.1" node-gyp-build "^4.2.3" weak-lru-cache "^0.3.9" @@ -20344,21 +20358,28 @@ ms@2.1.1, ms@^2.0.0, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== -msgpackr-extract@^0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-0.3.5.tgz#0f206da058bd3dad0f8605d324de001a8f4de967" - integrity sha512-zHhstybu+m/j3H6CVBMcILVIzATK6dWRGtlePJjsnSAj8kLT5joMa9i0v21Uc80BPNDcwFsnG/dz2318tfI81w== +msgpackr-extract@^0.3.5, msgpackr-extract@^0.3.6: + version "0.3.6" + resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-0.3.6.tgz#f20c0a278e44377471b1fa2a3a75a32c87693755" + integrity sha512-ASUrKn0MEFp2onn+xUBQhCNR6+RzzQAcs6p0RqKQ9sfqOZjzQ21a+ASyzgh+QAJrKcWBiZLN6L4+iXKPJV6pXg== dependencies: nan "^2.14.1" node-gyp-build "^4.2.3" -msgpackr@^0.5.3, msgpackr@^0.5.4: +msgpackr@^0.5.3: version "0.5.4" resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-0.5.4.tgz#c21c03d5e132d2e54d0b9ced02a75b1f48413380" integrity sha512-ILEWtIWwd5ESWHKoVjJ4GP7JWkpuAUJ20qi2j2qEC6twecBmK4E6YG3QW847OpmvdAhMJGq2LoDJRn/kNERTeQ== optionalDependencies: msgpackr-extract "^0.3.5" +msgpackr@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-0.6.0.tgz#57f75f80247ed3bcb937b7b5b0c7ef48123bee80" + integrity sha512-GF+hXvh1mn9f43ndEigmyTwomeJ/5OQWaxJTMeFrXouGTCYvzEtnF7Bd1DTCxOHXO85BeWFgUVA7Ev61R2KkVw== + optionalDependencies: + msgpackr-extract "^0.3.6" + multicast-dns-service-types@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" @@ -21311,7 +21332,7 @@ opn@^5.5.0: dependencies: is-wsl "^1.1.0" -oppsy@2.x.x, oppsy@^2.0.0: +oppsy@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/oppsy/-/oppsy-2.0.0.tgz#3a194517adc24c3c61cdc56f35f4537e93a35e34" integrity sha1-OhlFF63CTDxhzcVvNfRTfpOjXjQ= @@ -22732,7 +22753,7 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -pumpify@1.3.x, pumpify@^1.3.3, pumpify@^1.3.5: +pumpify@^1.3.3, pumpify@^1.3.5: version "1.3.6" resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.3.6.tgz#00d40e5ded0a3bf1e0788b1c0cf426a42882ab64" integrity sha512-BurGAcvezsINL5US9T9wGHHcLNrG6MCp//ECtxron3vcR+Rfx5Anqq7HbZXNJvFQli8FGVsWCAvywEJFV5Hx/Q== @@ -25422,10 +25443,10 @@ shelljs@^0.6.0: resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.6.1.tgz#ec6211bed1920442088fe0f70b2837232ed2c8a8" integrity sha1-7GIRvtGSBEIIj+D3Cyg3Iy7SyKg= -shelljs@^0.8.3: - version "0.8.3" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.3.tgz#a7f3319520ebf09ee81275b2368adb286659b097" - integrity sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A== +shelljs@^0.8.3, shelljs@^0.8.4: + version "0.8.4" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" + integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ== dependencies: glob "^7.0.0" interpret "^1.0.0" From 23dccb7c1cd9338910a65d5a7e73cda61ecebe30 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Wed, 2 Dec 2020 17:41:19 -0500 Subject: [PATCH 24/40] [Snapshot Restore] Fix initial policy form state (#83928) --- .../client_integration/policy_edit.test.ts | 48 ++-- .../sections/policy_edit/policy_edit.tsx | 18 +- .../server/routes/api/validate_schemas.ts | 8 - .../api_integration/apis/management/index.js | 1 + .../apis/management/snapshot_restore/index.ts | 12 + .../snapshot_restore/lib/elasticsearch.ts | 89 +++++++ .../management/snapshot_restore/lib/index.ts | 7 + .../snapshot_restore/snapshot_restore.ts | 234 ++++++++++++++++++ .../api_integration/services/legacy_es.js | 3 +- 9 files changed, 395 insertions(+), 25 deletions(-) create mode 100644 x-pack/test/api_integration/apis/management/snapshot_restore/index.ts create mode 100644 x-pack/test/api_integration/apis/management/snapshot_restore/lib/elasticsearch.ts create mode 100644 x-pack/test/api_integration/apis/management/snapshot_restore/lib/index.ts create mode 100644 x-pack/test/api_integration/apis/management/snapshot_restore/snapshot_restore.ts diff --git a/x-pack/plugins/snapshot_restore/__jest__/client_integration/policy_edit.test.ts b/x-pack/plugins/snapshot_restore/__jest__/client_integration/policy_edit.test.ts index 7c095256bd10f..28d4ad5aceb2d 100644 --- a/x-pack/plugins/snapshot_restore/__jest__/client_integration/policy_edit.test.ts +++ b/x-pack/plugins/snapshot_restore/__jest__/client_integration/policy_edit.test.ts @@ -124,7 +124,8 @@ describe('', () => { const { snapshotName } = POLICY_EDIT; // Complete step 1, change snapshot name - form.setInputValue('snapshotNameInput', `${snapshotName}-edited`); + const editedSnapshotName = `${snapshotName}-edited`; + form.setInputValue('snapshotNameInput', editedSnapshotName); actions.clickNextButton(); // Complete step 2, enable ignore unavailable indices switch @@ -143,20 +144,24 @@ describe('', () => { const latestRequest = server.requests[server.requests.length - 1]; + const { name, isManagedPolicy, schedule, repository, retention } = POLICY_EDIT; + const expected = { - ...POLICY_EDIT, - ...{ - config: { - ignoreUnavailable: true, - }, - retention: { - ...POLICY_EDIT.retention, - expireAfterValue: Number(EXPIRE_AFTER_VALUE), - expireAfterUnit: EXPIRE_AFTER_UNIT, - }, - snapshotName: `${POLICY_EDIT.snapshotName}-edited`, + name, + isManagedPolicy, + schedule, + repository, + config: { + ignoreUnavailable: true, + }, + retention: { + ...retention, + expireAfterValue: Number(EXPIRE_AFTER_VALUE), + expireAfterUnit: EXPIRE_AFTER_UNIT, }, + snapshotName: editedSnapshotName, }; + expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual(expected); }); @@ -180,10 +185,25 @@ describe('', () => { const latestRequest = server.requests[server.requests.length - 1]; + const { + name, + isManagedPolicy, + schedule, + repository, + retention, + config, + snapshotName, + } = POLICY_EDIT; + const expected = { - ...POLICY_EDIT, + name, + isManagedPolicy, + schedule, + repository, + config, + snapshotName, retention: { - ...POLICY_EDIT.retention, + ...retention, expireAfterValue: Number(EXPIRE_AFTER_VALUE), expireAfterUnit: TIME_UNITS.DAY, // default value }, diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/policy_edit/policy_edit.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/policy_edit/policy_edit.tsx index 7af663b29957d..a119c96e0a1ec 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/policy_edit/policy_edit.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/policy_edit/policy_edit.tsx @@ -64,8 +64,22 @@ export const PolicyEdit: React.FunctionComponent { - if (policyData && policyData.policy) { - setPolicy(policyData.policy); + if (policyData?.policy) { + const { policy: policyToEdit } = policyData; + + // The policy response includes data not pertinent to the form + // that we need to remove, e.g., lastSuccess, lastFailure, stats + const policyFormData: SlmPolicyPayload = { + name: policyToEdit.name, + snapshotName: policyToEdit.snapshotName, + schedule: policyToEdit.schedule, + repository: policyToEdit.repository, + config: policyToEdit.config, + retention: policyToEdit.retention, + isManagedPolicy: policyToEdit.isManagedPolicy, + }; + + setPolicy(policyFormData); } }, [policyData]); diff --git a/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts b/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts index e5df0ec33db0b..7a13b4ac27caa 100644 --- a/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts +++ b/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts @@ -26,20 +26,12 @@ const snapshotRetentionSchema = schema.object({ export const policySchema = schema.object({ name: schema.string(), - version: schema.maybe(schema.number()), - modifiedDate: schema.maybe(schema.string()), - modifiedDateMillis: schema.maybe(schema.number()), snapshotName: schema.string(), schedule: schema.string(), repository: schema.string(), - nextExecution: schema.maybe(schema.string()), - nextExecutionMillis: schema.maybe(schema.number()), config: schema.maybe(snapshotConfigSchema), retention: schema.maybe(snapshotRetentionSchema), isManagedPolicy: schema.boolean(), - stats: schema.maybe(schema.object({}, { unknowns: 'allow' })), - lastFailure: schema.maybe(schema.object({}, { unknowns: 'allow' })), - lastSuccess: schema.maybe(schema.object({}, { unknowns: 'allow' })), }); const fsRepositorySettings = schema.object({ diff --git a/x-pack/test/api_integration/apis/management/index.js b/x-pack/test/api_integration/apis/management/index.js index 5afb9cfba9f5f..7b6deb0c3892b 100644 --- a/x-pack/test/api_integration/apis/management/index.js +++ b/x-pack/test/api_integration/apis/management/index.js @@ -13,5 +13,6 @@ export default function ({ loadTestFile }) { loadTestFile(require.resolve('./index_management')); loadTestFile(require.resolve('./index_lifecycle_management')); loadTestFile(require.resolve('./ingest_pipelines')); + loadTestFile(require.resolve('./snapshot_restore')); }); } diff --git a/x-pack/test/api_integration/apis/management/snapshot_restore/index.ts b/x-pack/test/api_integration/apis/management/snapshot_restore/index.ts new file mode 100644 index 0000000000000..f0eea0f960b4b --- /dev/null +++ b/x-pack/test/api_integration/apis/management/snapshot_restore/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Snapshot and Restore', () => { + loadTestFile(require.resolve('./snapshot_restore')); + }); +} diff --git a/x-pack/test/api_integration/apis/management/snapshot_restore/lib/elasticsearch.ts b/x-pack/test/api_integration/apis/management/snapshot_restore/lib/elasticsearch.ts new file mode 100644 index 0000000000000..932df405dde12 --- /dev/null +++ b/x-pack/test/api_integration/apis/management/snapshot_restore/lib/elasticsearch.ts @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +interface SlmPolicy { + name: string; + snapshotName: string; + schedule: string; + repository: string; + isManagedPolicy: boolean; + config?: { + indices?: string | string[]; + ignoreUnavailable?: boolean; + includeGlobalState?: boolean; + partial?: boolean; + metadata?: Record; + }; + retention?: { + expireAfterValue?: number | ''; + expireAfterUnit?: string; + maxCount?: number | ''; + minCount?: number | ''; + }; +} + +/** + * Helpers to create and delete SLM policies on the Elasticsearch instance + * during our tests. + * @param {ElasticsearchClient} es The Elasticsearch client instance + */ +export const registerEsHelpers = (getService: FtrProviderContext['getService']) => { + let policiesCreated: string[] = []; + + const es = getService('legacyEs'); + + const createRepository = (repoName: string) => { + return es.snapshot.createRepository({ + repository: repoName, + body: { + type: 'fs', + settings: { + location: '/tmp/', + }, + }, + verify: false, + }); + }; + + const createPolicy = (policy: SlmPolicy, cachePolicy?: boolean) => { + if (cachePolicy) { + policiesCreated.push(policy.name); + } + + return es.sr.updatePolicy({ + name: policy.name, + body: policy, + }); + }; + + const getPolicy = (policyName: string) => { + return es.sr.policy({ + name: policyName, + human: true, + }); + }; + + const deletePolicy = (policyName: string) => es.sr.deletePolicy({ name: policyName }); + + const cleanupPolicies = () => + Promise.all(policiesCreated.map(deletePolicy)) + .then(() => { + policiesCreated = []; + }) + .catch((err) => { + // eslint-disable-next-line no-console + console.log(`[Cleanup error] Error deleting ES resources: ${err.message}`); + }); + + return { + createRepository, + createPolicy, + deletePolicy, + cleanupPolicies, + getPolicy, + }; +}; diff --git a/x-pack/test/api_integration/apis/management/snapshot_restore/lib/index.ts b/x-pack/test/api_integration/apis/management/snapshot_restore/lib/index.ts new file mode 100644 index 0000000000000..66ea0fe40c4ce --- /dev/null +++ b/x-pack/test/api_integration/apis/management/snapshot_restore/lib/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { registerEsHelpers } from './elasticsearch'; diff --git a/x-pack/test/api_integration/apis/management/snapshot_restore/snapshot_restore.ts b/x-pack/test/api_integration/apis/management/snapshot_restore/snapshot_restore.ts new file mode 100644 index 0000000000000..575da0db2a759 --- /dev/null +++ b/x-pack/test/api_integration/apis/management/snapshot_restore/snapshot_restore.ts @@ -0,0 +1,234 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { registerEsHelpers } from './lib'; + +const API_BASE_PATH = '/api/snapshot_restore'; +const REPO_NAME = 'test_repo'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + const { + createRepository, + createPolicy, + deletePolicy, + cleanupPolicies, + getPolicy, + } = registerEsHelpers(getService); + + describe('Snapshot Lifecycle Management', function () { + before(async () => { + try { + await createRepository(REPO_NAME); + } catch (err) { + // eslint-disable-next-line no-console + console.log('[Setup error] Error creating repository'); + throw err; + } + }); + + after(async () => { + await cleanupPolicies(); + }); + + describe('Create', () => { + const POLICY_NAME = 'test_create_policy'; + const REQUIRED_FIELDS_POLICY_NAME = 'test_create_required_fields_policy'; + + after(async () => { + // Clean up any policies created in test cases + await Promise.all([POLICY_NAME, REQUIRED_FIELDS_POLICY_NAME].map(deletePolicy)).catch( + (err) => { + // eslint-disable-next-line no-console + console.log(`[Cleanup error] Error deleting policies: ${err.message}`); + throw err; + } + ); + }); + + it('should create a SLM policy', async () => { + const { body } = await supertest + .post(`${API_BASE_PATH}/policies`) + .set('kbn-xsrf', 'xxx') + .send({ + name: POLICY_NAME, + snapshotName: 'my_snapshot', + schedule: '0 30 1 * * ?', + repository: REPO_NAME, + config: { + indices: ['my_index'], + ignoreUnavailable: true, + partial: false, + metadata: { + meta: 'my_meta', + }, + }, + retention: { + expireAfterValue: 1, + expireAfterUnit: 'd', + maxCount: 10, + minCount: 5, + }, + isManagedPolicy: false, + }) + .expect(200); + + expect(body).to.eql({ + acknowledged: true, + }); + + const policyFromEs = await getPolicy(POLICY_NAME); + expect(policyFromEs[POLICY_NAME]).to.be.ok(); + expect(policyFromEs[POLICY_NAME].policy).to.eql({ + name: 'my_snapshot', + schedule: '0 30 1 * * ?', + repository: REPO_NAME, + config: { + indices: ['my_index'], + ignore_unavailable: true, + partial: false, + metadata: { + meta: 'my_meta', + }, + }, + retention: { + expire_after: '1d', + max_count: 10, + min_count: 5, + }, + }); + }); + + it('should create a policy with only required fields', async () => { + const { body } = await supertest + .post(`${API_BASE_PATH}/policies`) + .set('kbn-xsrf', 'xxx') + // Exclude config and retention + .send({ + name: REQUIRED_FIELDS_POLICY_NAME, + snapshotName: 'my_snapshot', + repository: REPO_NAME, + schedule: '0 30 1 * * ?', + isManagedPolicy: false, + }) + .expect(200); + + expect(body).to.eql({ + acknowledged: true, + }); + + const policyFromEs = await getPolicy(REQUIRED_FIELDS_POLICY_NAME); + expect(policyFromEs[REQUIRED_FIELDS_POLICY_NAME]).to.be.ok(); + expect(policyFromEs[REQUIRED_FIELDS_POLICY_NAME].policy).to.eql({ + name: 'my_snapshot', + repository: REPO_NAME, + schedule: '0 30 1 * * ?', + }); + }); + }); + + describe('Update', () => { + const POLICY_NAME = 'test_update_policy'; + const POLICY = { + name: POLICY_NAME, + snapshotName: 'my_snapshot', + schedule: '0 30 1 * * ?', + repository: REPO_NAME, + config: { + indices: ['my_index'], + ignoreUnavailable: true, + partial: false, + metadata: { + meta: 'my_meta', + }, + }, + retention: { + expireAfterValue: 1, + expireAfterUnit: 'd', + maxCount: 10, + minCount: 5, + }, + isManagedPolicy: false, + }; + + before(async () => { + // Create SLM policy that can be used to test PUT request + try { + await createPolicy(POLICY, true); + } catch (err) { + // eslint-disable-next-line no-console + console.log('[Setup error] Error creating policy'); + throw err; + } + }); + + it('should allow an existing policy to be updated', async () => { + const uri = `${API_BASE_PATH}/policies/${POLICY_NAME}`; + + const { body } = await supertest + .put(uri) + .set('kbn-xsrf', 'xxx') + .send({ + ...POLICY, + schedule: '0 0 0 ? * 7', + }) + .expect(200); + + expect(body).to.eql({ + acknowledged: true, + }); + + const policyFromEs = await getPolicy(POLICY_NAME); + expect(policyFromEs[POLICY_NAME]).to.be.ok(); + expect(policyFromEs[POLICY_NAME].policy).to.eql({ + name: 'my_snapshot', + schedule: '0 0 0 ? * 7', + repository: REPO_NAME, + config: { + indices: ['my_index'], + ignore_unavailable: true, + partial: false, + metadata: { + meta: 'my_meta', + }, + }, + retention: { + expire_after: '1d', + max_count: 10, + min_count: 5, + }, + }); + }); + + it('should allow optional fields to be removed', async () => { + const uri = `${API_BASE_PATH}/policies/${POLICY_NAME}`; + const { retention, config, ...requiredFields } = POLICY; + + const { body } = await supertest + .put(uri) + .set('kbn-xsrf', 'xxx') + .send(requiredFields) + .expect(200); + + expect(body).to.eql({ + acknowledged: true, + }); + + const policyFromEs = await getPolicy(POLICY_NAME); + expect(policyFromEs[POLICY_NAME]).to.be.ok(); + expect(policyFromEs[POLICY_NAME].policy).to.eql({ + name: 'my_snapshot', + schedule: '0 30 1 * * ?', + repository: REPO_NAME, + }); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/services/legacy_es.js b/x-pack/test/api_integration/services/legacy_es.js index 04b991151034a..c184a87365977 100644 --- a/x-pack/test/api_integration/services/legacy_es.js +++ b/x-pack/test/api_integration/services/legacy_es.js @@ -10,6 +10,7 @@ import * as legacyElasticsearch from 'elasticsearch'; import { elasticsearchClientPlugin as securityEsClientPlugin } from '../../../plugins/security/server/elasticsearch/elasticsearch_client_plugin'; import { elasticsearchJsPlugin as indexManagementEsClientPlugin } from '../../../plugins/index_management/server/client/elasticsearch'; +import { elasticsearchJsPlugin as snapshotRestoreEsClientPlugin } from '../../../plugins/snapshot_restore/server/client/elasticsearch_sr'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { DEFAULT_API_VERSION } from '../../../../src/core/server/elasticsearch/elasticsearch_config'; @@ -20,6 +21,6 @@ export function LegacyEsProvider({ getService }) { apiVersion: DEFAULT_API_VERSION, host: formatUrl(config.get('servers.elasticsearch')), requestTimeout: config.get('timeouts.esRequestTimeout'), - plugins: [securityEsClientPlugin, indexManagementEsClientPlugin], + plugins: [securityEsClientPlugin, indexManagementEsClientPlugin, snapshotRestoreEsClientPlugin], }); } From a5dd5b6998163274d4fabf3710b4331d440e68ec Mon Sep 17 00:00:00 2001 From: Sandra Gonzales Date: Wed, 2 Dec 2020 18:33:27 -0500 Subject: [PATCH 25/40] [Fleet] EPM support to handle uploaded file paths (#84708) * modify file route to handle uploaded packge file paths * update messaging * improve tests * fix bug and add test to check the version of the uploaded package before failing * fix similar bug for getting package info from registry when a different version is uploaded --- .../fleet/server/routes/epm/handlers.ts | 61 +++-- .../fleet/server/services/epm/packages/get.ts | 5 +- .../fleet_api_integration/apis/epm/file.ts | 233 +++++++++++++----- .../fleet_api_integration/apis/epm/get.ts | 19 ++ .../apache_0.1.4.tar.gz | Bin 582173 -> 582747 bytes .../direct_upload_packages/apache_0.1.4.zip | Bin 607403 -> 607436 bytes 6 files changed, 236 insertions(+), 82 deletions(-) diff --git a/x-pack/plugins/fleet/server/routes/epm/handlers.ts b/x-pack/plugins/fleet/server/routes/epm/handlers.ts index aa6160bbd8914..05060c9d863aa 100644 --- a/x-pack/plugins/fleet/server/routes/epm/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/epm/handlers.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import { TypeOf } from '@kbn/config-schema'; +import mime from 'mime-types'; +import path from 'path'; import { RequestHandler, ResponseHeaders, KnownHeaders } from 'src/core/server'; import { GetInfoResponse, @@ -43,6 +45,8 @@ import { import { defaultIngestErrorHandler, ingestErrorToResponseOptions } from '../../errors'; import { splitPkgKey } from '../../services/epm/registry'; import { licenseService } from '../../services'; +import { getArchiveEntry } from '../../services/epm/archive/cache'; +import { bufferToStream } from '../../services/epm/streams'; export const getCategoriesHandler: RequestHandler< undefined, @@ -102,22 +106,51 @@ export const getFileHandler: RequestHandler { try { const { pkgName, pkgVersion, filePath } = request.params; - const registryResponse = await getFile(`/package/${pkgName}/${pkgVersion}/${filePath}`); - - const headersToProxy: KnownHeaders[] = ['content-type', 'cache-control']; - const proxiedHeaders = headersToProxy.reduce((headers, knownHeader) => { - const value = registryResponse.headers.get(knownHeader); - if (value !== null) { - headers[knownHeader] = value; + const savedObjectsClient = context.core.savedObjects.client; + const savedObject = await getInstallationObject({ savedObjectsClient, pkgName }); + const pkgInstallSource = savedObject?.attributes.install_source; + // TODO: when package storage is available, remove installSource check and check cache and storage, remove registry call + if (pkgInstallSource === 'upload' && pkgVersion === savedObject?.attributes.version) { + const headerContentType = mime.contentType(path.extname(filePath)); + if (!headerContentType) { + return response.custom({ + body: `unknown content type for file: ${filePath}`, + statusCode: 400, + }); } - return headers; - }, {} as ResponseHeaders); + const archiveFile = getArchiveEntry(`${pkgName}-${pkgVersion}/${filePath}`); + if (!archiveFile) { + return response.custom({ + body: `uploaded package file not found: ${filePath}`, + statusCode: 404, + }); + } + const headers: ResponseHeaders = { + 'cache-control': 'max-age=10, public', + 'content-type': headerContentType, + }; + return response.custom({ + body: bufferToStream(archiveFile), + statusCode: 200, + headers, + }); + } else { + const registryResponse = await getFile(`/package/${pkgName}/${pkgVersion}/${filePath}`); + const headersToProxy: KnownHeaders[] = ['content-type', 'cache-control']; + const proxiedHeaders = headersToProxy.reduce((headers, knownHeader) => { + const value = registryResponse.headers.get(knownHeader); + if (value !== null) { + headers[knownHeader] = value; + } + return headers; + }, {} as ResponseHeaders); - return response.custom({ - body: registryResponse.body, - statusCode: registryResponse.status, - headers: proxiedHeaders, - }); + return response.custom({ + body: registryResponse.body, + statusCode: registryResponse.status, + headers: proxiedHeaders, + }); + } } catch (error) { return defaultIngestErrorHandler({ error, response }); } diff --git a/x-pack/plugins/fleet/server/services/epm/packages/get.ts b/x-pack/plugins/fleet/server/services/epm/packages/get.ts index c10b26cbf0bd1..9b4b26d6fb8b3 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/get.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/get.ts @@ -98,7 +98,10 @@ export async function getPackageInfo(options: { const getPackageRes = await getPackageFromSource({ pkgName, pkgVersion, - pkgInstallSource: savedObject?.attributes.install_source, + pkgInstallSource: + savedObject?.attributes.version === pkgVersion + ? savedObject?.attributes.install_source + : 'registry', }); const paths = getPackageRes.paths; const packageInfo = getPackageRes.packageInfo; diff --git a/x-pack/test/fleet_api_integration/apis/epm/file.ts b/x-pack/test/fleet_api_integration/apis/epm/file.ts index ab89fceeb5b49..2823b236c0321 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/file.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/file.ts @@ -4,6 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ +import fs from 'fs'; +import path from 'path'; +import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; import { warnAndSkipTest } from '../../helpers'; @@ -14,79 +17,175 @@ export default function ({ getService }: FtrProviderContext) { const server = dockerServers.get('registry'); describe('EPM - package file', () => { - it('fetches a .png screenshot image', async function () { - if (server.enabled) { - await supertest - .get('/api/fleet/epm/packages/filetest/0.1.0/img/screenshots/metricbeat_dashboard.png') - .set('kbn-xsrf', 'xxx') - .expect('Content-Type', 'image/png') - .expect(200); - } else { - warnAndSkipTest(this, log); - } - }); + describe('it gets files from registry', () => { + it('fetches a .png screenshot image', async function () { + if (server.enabled) { + const res = await supertest + .get('/api/fleet/epm/packages/filetest/0.1.0/img/screenshots/metricbeat_dashboard.png') + .set('kbn-xsrf', 'xxx') + .expect('Content-Type', 'image/png') + .expect(200); + expect(Buffer.isBuffer(res.body)).to.equal(true); + } else { + warnAndSkipTest(this, log); + } + }); - it('fetches an .svg icon image', async function () { - if (server.enabled) { - await supertest - .get('/api/fleet/epm/packages/filetest/0.1.0/img/logo.svg') - .set('kbn-xsrf', 'xxx') - .expect('Content-Type', 'image/svg+xml') - .expect(200); - } else { - warnAndSkipTest(this, log); - } - }); + it('fetches an .svg icon image', async function () { + if (server.enabled) { + const res = await supertest + .get('/api/fleet/epm/packages/filetest/0.1.0/img/logo.svg') + .set('kbn-xsrf', 'xxx') + .expect('Content-Type', 'image/svg+xml') + .expect(200); + expect(Buffer.isBuffer(res.body)).to.equal(true); + } else { + warnAndSkipTest(this, log); + } + }); - it('fetches a .json kibana visualization file', async function () { - if (server.enabled) { - await supertest - .get( - '/api/fleet/epm/packages/filetest/0.1.0/kibana/visualization/sample_visualization.json' - ) - .set('kbn-xsrf', 'xxx') - .expect('Content-Type', 'application/json; charset=utf-8') - .expect(200); - } else { - warnAndSkipTest(this, log); - } - }); + it('fetches a .json kibana visualization file', async function () { + if (server.enabled) { + const res = await supertest + .get( + '/api/fleet/epm/packages/filetest/0.1.0/kibana/visualization/sample_visualization.json' + ) + .set('kbn-xsrf', 'xxx') + .expect('Content-Type', 'application/json; charset=utf-8') + .expect(200); + expect(typeof res.body).to.equal('object'); + } else { + warnAndSkipTest(this, log); + } + }); - it('fetches a .json kibana dashboard file', async function () { - if (server.enabled) { - await supertest - .get('/api/fleet/epm/packages/filetest/0.1.0/kibana/dashboard/sample_dashboard.json') - .set('kbn-xsrf', 'xxx') - .expect('Content-Type', 'application/json; charset=utf-8') - .expect(200); - } else { - warnAndSkipTest(this, log); - } - }); + it('fetches a .json kibana dashboard file', async function () { + if (server.enabled) { + const res = await supertest + .get('/api/fleet/epm/packages/filetest/0.1.0/kibana/dashboard/sample_dashboard.json') + .set('kbn-xsrf', 'xxx') + .expect('Content-Type', 'application/json; charset=utf-8') + .expect(200); + expect(typeof res.body).to.equal('object'); + } else { + warnAndSkipTest(this, log); + } + }); - it('fetches a .json search file', async function () { - if (server.enabled) { + it('fetches a .json search file', async function () { + if (server.enabled) { + const res = await supertest + .get('/api/fleet/epm/packages/filetest/0.1.0/kibana/search/sample_search.json') + .set('kbn-xsrf', 'xxx') + .expect('Content-Type', 'application/json; charset=utf-8') + .expect(200); + expect(typeof res.body).to.equal('object'); + } else { + warnAndSkipTest(this, log); + } + }); + }); + describe('it gets files from an uploaded package', () => { + before(async () => { + if (!server.enabled) return; + const testPkgArchiveTgz = path.join( + path.dirname(__filename), + '../fixtures/direct_upload_packages/apache_0.1.4.tar.gz' + ); + const buf = fs.readFileSync(testPkgArchiveTgz); await supertest - .get('/api/fleet/epm/packages/filetest/0.1.0/kibana/search/sample_search.json') - .set('kbn-xsrf', 'xxx') - .expect('Content-Type', 'application/json; charset=utf-8') + .post(`/api/fleet/epm/packages`) + .set('kbn-xsrf', 'xxxx') + .type('application/gzip') + .send(buf) .expect(200); - } else { - warnAndSkipTest(this, log); - } + }); + after(async () => { + if (!server.enabled) return; + await supertest.delete(`/api/fleet/epm/packages/apache-0.1.4`).set('kbn-xsrf', 'xxxx'); + }); + it('fetches a .png screenshot image', async function () { + if (server.enabled) { + const res = await supertest + .get('/api/fleet/epm/packages/apache/0.1.4/img/kibana-apache-test.png') + .set('kbn-xsrf', 'xxx') + .expect('Content-Type', 'image/png') + .expect(200); + expect(Buffer.isBuffer(res.body)).to.equal(true); + } else { + warnAndSkipTest(this, log); + } + }); + it('fetches the logo', async function () { + if (server.enabled) { + const res = await supertest + .get('/api/fleet/epm/packages/apache/0.1.4/img/logo_apache_test.svg') + .set('kbn-xsrf', 'xxx') + .expect('Content-Type', 'image/svg+xml') + .expect(200); + await supertest + .get('/api/fleet/epm/packages/apache/0.1.4/img/logo_apache.svg') + .set('kbn-xsrf', 'xxx') + .expect(404); + expect(Buffer.isBuffer(res.body)).to.equal(true); + } else { + warnAndSkipTest(this, log); + } + }); + + it('fetches a .json kibana dashboard file', async function () { + if (server.enabled) { + const res = await supertest + .get( + '/api/fleet/epm/packages/apache/0.1.4/kibana/dashboard/apache-Logs-Apache-Dashboard-ecs-new.json' + ) + .set('kbn-xsrf', 'xxx') + .expect('Content-Type', 'application/json; charset=utf-8') + .expect(200); + expect(typeof res.body).to.equal('object'); + } else { + warnAndSkipTest(this, log); + } + }); + + it('fetches a README file', async function () { + if (server.enabled) { + const res = await supertest + .get('/api/fleet/epm/packages/apache/0.1.4/docs/README.md') + .set('kbn-xsrf', 'xxx') + .expect('Content-Type', 'text/markdown; charset=utf-8') + .expect(200); + expect(res.text).to.equal('# Apache Uploaded Test Integration'); + } else { + warnAndSkipTest(this, log); + } + }); + + it('fetches the logo of a not uploaded (and installed) version from the registry when another version is uploaded (and installed)', async function () { + if (server.enabled) { + const res = await supertest + .get('/api/fleet/epm/packages/apache/0.1.3/img/logo_apache.svg') + .set('kbn-xsrf', 'xxx') + .expect('Content-Type', 'image/svg+xml') + .expect(200); + expect(Buffer.isBuffer(res.body)).to.equal(true); + } else { + warnAndSkipTest(this, log); + } + }); }); - }); - // Disabled for now as we don't serve prebuilt index patterns in current packages. - // it('fetches an .json index pattern file', async function () { - // if (server.enabled) { - // await supertest - // .get('/api/fleet/epm/packages/filetest/0.1.0/kibana/index-pattern/sample-*.json') - // .set('kbn-xsrf', 'xxx') - // .expect('Content-Type', 'application/json; charset=utf-8') - // .expect(200); - // } else { - // warnAndSkipTest(this, log); - // } - // }); + // Disabled for now as we don't serve prebuilt index patterns in current packages. + // it('fetches an .json index pattern file', async function () { + // if (server.enabled) { + // await supertest + // .get('/api/fleet/epm/packages/filetest/0.1.0/kibana/index-pattern/sample-*.json') + // .set('kbn-xsrf', 'xxx') + // .expect('Content-Type', 'application/json; charset=utf-8') + // .expect(200); + // } else { + // warnAndSkipTest(this, log); + // } + // }); + }); } diff --git a/x-pack/test/fleet_api_integration/apis/epm/get.ts b/x-pack/test/fleet_api_integration/apis/epm/get.ts index 53982affa128c..a6be50804aa5e 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/get.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/get.ts @@ -71,6 +71,25 @@ export default function (providerContext: FtrProviderContext) { warnAndSkipTest(this, log); } }); + it('returns correct package info from registry if a different version is installed by upload', async function () { + if (server.enabled) { + const buf = fs.readFileSync(testPkgArchiveZip); + await supertest + .post(`/api/fleet/epm/packages`) + .set('kbn-xsrf', 'xxxx') + .type('application/zip') + .send(buf) + .expect(200); + + const res = await supertest.get(`/api/fleet/epm/packages/apache-0.1.3`).expect(200); + const packageInfo = res.body.response; + expect(packageInfo.description).to.equal('Apache Integration'); + expect(packageInfo.download).to.not.equal(undefined); + await uninstallPackage(testPkgKey); + } else { + warnAndSkipTest(this, log); + } + }); it('returns a 500 for a package key without a proper name', async function () { if (server.enabled) { await supertest.get('/api/fleet/epm/packages/-0.1.0').expect(500); diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/direct_upload_packages/apache_0.1.4.tar.gz b/x-pack/test/fleet_api_integration/apis/fixtures/direct_upload_packages/apache_0.1.4.tar.gz index c5d3607e05cb8cc6673a4e75ca08e4104bfabf6e..4dbd2f223d5064c424d1a1e34cb735e07e46f447 100644 GIT binary patch delta 476506 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⋘2~~-{=Mq3G?cNCQ$w@B0kdnQhim1%V`$li8Buvw%D14ibN93~ z*S|rvvIv7wwsTTRUoGbajyRN(^;gq+zfHaK`;s{rsm_Dsm&1e>Rkc{%yCyfecOg#y z7lq?yUhHDlh4=6$iC;MRU$B2hYEjSlo&X}nUjZTede;Acudt~Yo0;gOUt3#iViWi7 zv*uS(nAhoT^&dYjyAG9pP`_t56ItI;qR}+!vw!l-|AAn<|3L6~VGh~<#}eD>nwW*W z?;>&GzXFA=pMD0u2a_xQ);Kg)dGBIa1q*nE=Q6fr|LAv%AojmF+C+ag;az*sApaFe z+*g`He^1Ri%1uSBfPsEiHcZ=Q-lLg>)~r?~gWK`jkz-b}E0~GG2QuoCrl{daR;pjV z!RB}h^ORkik|>58Ha*}X^?Y%wF8}_$xcGpF!`VyoPk~x|GlK4~wmy~{>sv88{J?wkS&^<^!e2%`!JPV@i@oYe5PgH1+OQo0|Hrx8=PMz8i^ zT>zjyrUxzcMt%;0eh=ce;BHLHoU_E=vEGP^W&pG`?%(V6EWC^?s8pS09=wk~0W+IX zMQ1CyitONv?D*)}X|o(%#ETUNo8xa5~Ut1?!ERRbUE~3P@!`Rsq81C7%32{Kiwal}h6B8AGd@4vEEXuXS{zgW}Zxx7s&XT0F`}|5CYgp29E*!2ywGPE0W& zNGR$Vlg8DxJtBRbn(6!S{Sfm3jiw;s0SP-ZE*H_fLU|Elt5PYqZTcldu*c$@yez*m zCT3w)%v}7he1b5WI3s$Kk=mHn%B;l9RA_l(NBy|utVrp&1Lb-Y&&s;nl}CA#FN?h; z*%E~L%XiWbq zut-=9U2<~aX{T=cC4L#%t&Oi=l!>1v&5+{eDvf_!sZ*tvnfAeRSVB&uNasXL$uxWJ zGYr1v++R=N?!e;d^_nvK;WDRW9sMm(aFvNCP$9Zbr}cB+gb&w`}?0ITfkW2rip~zud{^ zj^|<6$fj_Za81w`Q$kysShHsHShFoF;DCQWnI!g4-D=_x#Z2pD<1!(N!DTAPET2Px zpPC+MA(I2rSn|(Ta?7Bl(v6F zJSgfPsB@7 zw=U4eXS<>n*Y+qan)u$jmjb=(C$4{zS*N(hL7GOnbLL}Af2A(|a4b@j5;o8jxJHbS zRJDTQ4#Vk&$k?o$0>1M2W`_8awGHn3b0E*O5axVFOub|b{#2h~CAa^vdUq0t)I3co zI(oo!ME+mtUIoU%(UI9|fsTOB^D`>%4H}2_5(Fb-H7QtQe|s@;eYIR7-&ud@D-V@= z(h3Dx%_LzT1g9gZIV+7L_t`uAPfSgIV|)&xrWMAKV9jr z{_9UUwZlyzBHWtA)&WZ1fQC@2MwfUDU=RL zvkc`fgX)J*OC>}h!J%wY%+0uD%-Y=KmqqR}FE+QKIa!wpSLNn;?$L7CPdQC?4J$8xiBUxEy zj{b{cE@^;?i;Mely(^XRLp#_)!3_PXi8VTFf(1 zLcV*FEc^l(fWJucB6pWqSaEyEUkzPfacIc<#ZP$E3=d#O-;50{xin&eWy4S;BNUfk zy*IKOS0;^(HW)C=-l@Fb=pDtVP0(#*7b~NPsRMgCl>!aUXb5-{{j~&h;gx{PROo4I z>Gv#PT^zz=kKun5Z6k9ktSEASnHqQtiMTyy_ujU%DzRVtW-ttD%DgFYzRKI`2kQao zIeF2ZXG&v^9hrwr+$C7b_ZzflhlQB;~#pl9@Acyn?MumSq|HsP z`RDS$%_Jhf%LJ2#TjQ6yyB=tZ{JT9o7L7nglN?hx9o1E8T8(+H_KOkuy%hMD_HVhbY+}x_=8r5Yi(2a%Sm-t6@q@h)12DVSKAXG6|X_zarDrng1%oX zjg@&xmia(qt{G#Md6JXnr(vL4v`39bGxsH0EiJB`FH(e>?LdzxX-R+UV)wYq+Si_lObE(Y!>EdNwA4yT zjkaHf*kATFs(s>z@)#-VsMW5$I?8)Cbi?St`6}YLnlzIHrYZWAsSL`|bU4TfLq%^^ z>e8!L(yCQoR*QmoZ1&PpkHJdeZAz5-V;2Y3xGI>)kgw;m$- zyZ)6lE~w^;-zQm?kEErZ6{T5o(ko7=9vjkGUtDwkW`DArm%ROw3t;9Wqa{LPwv}$PX115 zuwj9YDpL-f7`&-h={KGu)8;Rm#jUtW8l4HblAdSgaBE@<#&V?3i%v2#p%$kJ;n2qF=w|IMcs!8*N> zz)PX08Rri6{rWX`ynIjA_0_apqIeDSK_#$yKzCqs%kR;Do}bSHrz-`&?J=P+pxmeq zeMuCiL+|$2SH6d#naNcB%7KA1dvkvtg^h0;e^xC+Jep-j*A1(d%4&DBxG0s>H3T`t z`0=l#=h>->cjjmchO>Uu4R>n2a&7T7)C!r{PX047(ZMWKYKK&6H}{Z~U(G2E{{5D` z#IET5ia%T|85*h=mcr#hC`VO9M#hWcL$B$Z&+?q5z8XdvL|PhsR|zoq21I}GFb?~| zf_XW(9hFv-YqwP;{$Tz1;(KbANahhzIhgl(nO26hJwao-O&Naen)I?i4f+~e)Ggj{ z8{zbiy+*{z*9NQF@AR%3#|M1|2hlH~>UP!@YQ#~NQa%_?1%^S07ulono23^9O_z#&*rVX)!@a3>Ifd zs@?JIW7*7wuNL=m=t@@G!N5AU>Eg5wiaI=iRj}dr9Q=ukWd@Jj(wBd&T?_(!#wTCh z1ioD&&1+f~a>HtAS`a=1VLh_tqSSeekw(o|CD8q8Eqq2|GhdtByl$j24scC}@~&Wo-qfBe zgE!fR#a_DJUTM{a_!A@R2IqdQKH*qS&aTopj2f=N5s}WZA5DK_%A#8T?x^OQmcnC0 z^t~`acc@UxPeVun%@=D$Pj~X2x4J0!@zKxs7jbdnCy%%!g!fbgWgF>3g{(!y9wGg$ zfeM#w5l_m#+Vz~SfBnqabUWA#r@oyS5Uwc;vseWcb}*}So6}?-j{M*~K^Hb>6?H(5 z4>sGLBs|!heHMRXt9Sf%vl9IyVmYR>*uey$R3t!>ZDrrYvB8|BvEe$+z-U_RLqO~I z6HIQ5Hy39j;GcSo>WgWwBQ|TOU@lj(e5ZT4LEJQdXj0G81|Ja1>7uOV{az*}Wp3qX z=FY)xl^)c8mzh01Jq;y{`O~3B7{Af+@U#p=)|lMu0^xrX0jV43ht8|<6r4&uN&CQv zRR9mKZba5~rP>@C#R2bOQ(5;Sl@dp;e%O8habJpa(GXD><;bsQ0QZIX$gHEN`E}Ca z7c1OeH=inL2Rd}AEIzf-GaXKv1~wjL(spB1dU-1xWVox9L40QS8S;kM6wc(Gxno;_ z3q_Y4;WK}&5|hRQjoeI}5(Rix^)V9yT*Jv;Z@Rc@!z}{V(O4^E-|FTy+Jzm)&15ez zJnXI~3WpHBXl4UdHhdz7uA^8#uoGb&J5Kt*_7|_!8qQ>%r=J{hA-AmPK9qz<=vGWt z@=$Ed6*q761b2Nh|FR4QO!)0L9|{7x7(QOYP&9vvvq3{?m^difArWJ+&)qs(Qx$zX zXf?x3iGW-#PsGyOUAQy143PMz%K5aeodE6+${I2`aU{eUBSK_D{CN}qo;J4y49C>Q zN)!wv3rQx7pAk=uZqur*dgAC_8Rt=Fv0D;#cz}q~Hemlv`=DxcyxfD4aacM$VIq!U zveSPoyMA)r3e~LoO;1=@5rww1LO|FlRiI&fZ3q>SVzD9zd$B>4w|uXNH<16O^Nq|? zdYa7#l>JgiaCec=w5iFQg@m?td5_Jjf8Z!s8Z~Fxs5mXQ*1~px??PX->dQ!f1g1tQD%U5TDOZ9ujdAr%B)u>g^kqXCnOn!`!20K6|1;r@k}!VC>~p7`{RjD>QTeyciBh5Z`{Ms+q63j&sd0bN_IJ6upC5l* zE(+PsPHt5b$IpgF!oclQH{A0-U1pQt+_sIU&FHHcLdu(o1G$5Abq=ySR#jE3U84pq zi2AsI2Sm+M5wq$GYQ1Y?Iw|^Eh!;RNh6Z-__~((?!bygpbWXbv{l`MP5f@#9ooU0! z7B8P~k-Tmg-n*U6pU6NzU~@84vyp#{FqI8tUqa!l*GCUso~tV=K9*ZqEk-T)pLZIYZ`xIgWC)` zgX=XgwH0(!mQ6{`L>+944y3uty;z!lsPo?#%#sV9ri!PLo-e9B;$$)ftEj1wcc?B; zW)NH&^T>T=89P|C4XV0p`ccrRd0UQ1W+dOccB*rDevTO6)tO$!cssZg7N(HjRH3qr zy^qs#+CjQ)d8%IevAn!oO)r1jN-9%(+w4i_Q}&LB(#A+lh1uLF`u8YT?Q%1|)HIYR zr`z1-Vp@RbjmBArwbyaaQSKTY%FP8&Z%j{T2wAQw$jrvehJ{htaD00Hm}dfq-`!+; za5j`;VhE_bYO7%)_I|s6>$+6de{(mtw1lsW z;$iK2>wLSv@7>%CyF5=`-(&t@ae6e%R8GAr3?06okz2SYO4}Rr_7D@y_u+0>9F-_0 zIILKCt25%Dw_@a312lhhwa#$6IJkAQDNs^I{qpD5+!_Fw_5A4bC7UdznKlu%3nw{Q zxP89@vy<0(TuTGV{@daP>m&BGE30cIQ;U3jf2CyiYKp?M4yCgl=8AlOH&s~ zJPq{#FQ{<=?_%aS>UN-b(YEYw5qV*HTXi%GSel(rRr&Vh!s~x|kv}OEG0>M9|B?%A zVv4D2wgw?xbi}{#ZdLO?k1gyL%iRT4k$roimW$Vdn=5%9M~>=elu-sb_}&jF_`KSu z-v{3qE20#zn0Vj)Hvkhrr{2Q}?XX=Vnn!HanfBi;OxWR{+-*XG^uDrP{nU!+J)T-$ zz{`^Q7I|Sbo!WnXNiMH{njD9noLf}3f~}gUBI3b-MnmmP`$aYb%;U3JdZ z5%a7Np}{6KELE2;zxe-j@>2De_@t%sVbmr1x~-;mw(iM`PAeS_kK(d2_`nflpc~&+ zJqk=~JQ!C5n#;jJJp?FTT|xu7ZnU&x{8MhJD;t}u?iYtQTAi0x;2D!O~rruI4Z`Y5|hi=q;ZZj`qUbm|I zB$t0(U_eH7tvQ?D@H{3>%^J3*&?b;Gr*83goM*>Bc-KHqE!SHA#@!3nzeKwx+kpH@ z^1ErWdaVKcYdlbETt5m5G4lfA3JdC!M~t(nn+@ka*l`Im=~${$931Mpq1)-C{vuIq zNsFmaQ6=U@o9&{0C5|t>14R$n?aIkC+2McYEpf5yxp-ip7pn}4K-!uU=(@Gw$b@}t zmxclk#YLs|wq2v&-URLqXkLWaY?nhH3R0*Fz0H`Tn+UWNTN|qWoSXQy zqkhP^ky0+s4g9l$-+UpBR`T&6L{7`urIGdHnf5Do8pnT1g;R7$KdJ@~9o^=aqgFYg*o9X&$x7qjI1q%3 znTbtM2RU%PMi{4MJ#lQkQxeo6`=p*J7aH)&lUdrURdmurtHI7NCMV~Xs36q+dMN6h z*$<+`>CeH~Lg!HpeH$F75?Gw`5ARyOh9+zbk>z6G^{ z$zRAM$BRrJBS2Gau`TUa9y@;?K(mMXZMMnP82dEcYM^GJ+b^?Ay`ST_eaf8a*biTiOiDZu z?CNmz`3CUdqsl1|Gm&w|h3r~sw4WxIEkA?O&uSh=-bP3#yQEpAgsOj?!l;UMR@b(| zz%zebmeEjp;pT}64GDuK7XU(=z0CY9Tg-TEn?CbEmCXka#d7QrJD%50-1+0F1X$|( zE(Qm2Lw8Lzh>^7sY%UHRV$Obp&ak$yY>+QKyW+0El-}!it~S!!`je&P_U+e5LJ&CR z@8G69@o53U(^;zDSZaULQ5fTf_2^2$9^%WqGYhI}sf+s3WANke;!i@OLAvbD3d={r zvx9J0J&ZiFz{ALx46|fzzCqA~>`EMrJTP9}v!Nd-5Yfm!wlRa_@YSY|T}BOz?Mr7l zP>AqB7pEj>ctnluXC6Pmg9ATnd>oNmxHpG61MhSL#xoYqe|~>>hxeG6Cx*98RZ~jV zEl3Re3=o;#SLuTBmOF4{l+!kyQt%_w$E?G7Q}@CK1iao1w#pGvFDPGpYKCR*rg z0Y%t5m=Fy*fzE#e8O}Mf3iHptOyQ?U|r%7X>EDaG+|J#cnr`?{{X9bqxxM>wt@vSaF#wEAfW z2Nq)ofcSecTf8n%;vi@;c^^Cz-GAKOxqIOT_Oj=dxMzQ{FYtNr@?O8#;k3}%u^@Pf zudaAK^|Z)h+`HmCI$(Q-@^bKIhRg#HLlA2Jpds`Hxn4|9EiTe44-Wh&urUJpxd;ZZ zIS@t31{Tj8Qzx#>ABc+)CA))*PAIb?L#X7lf~&o6N7Esn14cXAXl? zc;KEqY*>E{W|3DPAoXe3IV<5Kw|I6twDk0tFB#WQyE|}|d8=~uXPh}$kJT3A)&_P^ z!G$S7BXc@0%WQ796ULEEQbvy&gJSw4mE@qS3Yk~9nEPO)V%Xkfh79CCnXUV=tuBdo z_%;FfE<{V*`x!cLHFs-#mG}?sFHd?KJg$qm%-Mhc+b8?~^T~d&qkNA7uA|6InmTKRD02V*C$E0!z#{B*=*49MYkQUv*E?It}5>CN!Foh#D#H|+5YsGv(iQ_1M8chBN|iTS|FELvGedWUMND%U4uZc@{B)p z1(W~{s%oGZLR;sx{Q^C)@oFJf)T{}(n&nc^D1tfNP9~0#;`MZ-@^X0ZWGkMrD(J-J z`~>I(-HAIIsmn(3IUPQ({`!fX<<+=dl*WJc#qkQ|F<$!vx=mVpd?b1O6aFi_KXa1#G;|urAkE>P6w_sj>Z&!jx*`=D_k@l7fSs?=? zCwg#7?@9zpe8u2*oac}>=BRXzJa7kkxV7+u^>QkXUG`{B+=~DJJ7rC9=9YY15~P1h zU;bJh<)WJ==69y86YN`Rzk@l zn*&VVKl@di(@QDm8+Zi+k?p1-EAl?zk<%fl40yO3R%JjWeuXHrl$4X(1-Pn*X8>O z&wK>!Tlt92%%!h-`5Zf$lG-zGXr{>v$>TEf-4wo-GZ6VDWO4(L6DbvhQ+qCJ{F`B* z3}Eaih)(~ASToBp*_VImC4a-77<{c`8c-lhZPj=yzG5*vAXE6l@kQHFq1*lQ6kJJ> zOUz3A^)%-Zi7Lia#C?p6zM7^9Pl^|#O_jKk+Og5IB2Z7wb+|s#CDn{uA2W-}qVO7U z&pX^UvKq^>)zYc;LKrV1GXoLqG3}6uJv(<(AaJNT&-q3Wfz5xI*5x|_4b>YyU8+pg z8$#@DSZM@Zxzw+?VwUZLeOy=wI!^|E%KY;%qT_J8|A#3z>Qh6|h-4J96URW5__k`& z0o1iATe8<s^2r_4;tZf0Jk;A5Jk zL)X2Fb#|@n;5>iXvW{d(H`qteFE$2d4WMYn$YC%GflUxq;#+{RxmHY7vPY?@I`Dd$ z0N4LlWhtX1#Qt?|O)=ARN409?heV{G%SiFbru&%1$t$oZw)V!cI6i==j_r%W{Y*Xe z6zEd|GH>gU%gJf43NipuCj6QBu1<6Pe8xf?4ir;Fl6Zg2AUWF>>Q{6;3(j3hOp(T% z4G18{7tAe#PAEar_@pD~HFer9R4ZC1$~k*t#b&WRmohz5UUSOA23C}u;SYB0_F6iD z1C&{ToD;yk4EOxf|*`nv1&=BNMy+{EAo#Rw93ja7|%((jG&#rx1eG<70AJf!XH!(ZTt^LSMRhTxc0i~w@%-k8gQ4ck2}gL9 zvQvZbZ$ULUwg7IE|3ve%rBca8SYXO6`C*=TE4hDq)|xlM{$;a_4#Mk!$Ix419~*8r z4=n76xNEZa0@Hu{;|vPIk1O>uCy}L`4>W;qRxzp91Gp6Uvn@7bq{I^t9etE!mT>-_wtXBmHDw z0^E%`j8$Mq{%W@QCm@FU+MgqteHz8FODkwZN;CWl@bzS$S@K5f{%O9TR*YCwV2}5s zTS4cA*M(j0(QNSz!^E2$j4xFS48E-P^e}&Bz!n_;1NHM}2yt+=r&~SPu-0R6*Qiz) zp8^bDFMTY&Ol;0tE^F$v_t6+SrLCi2yfK*OB;GxtF_u@EWUGN=GL%WOPyF_ysAB~u zana4sl{#I%H?0pXT~fv?w#O~Z*ijK4lf2wJfZ zIBdUv)fAG$ft)@(uE@EjRNxjN*BB_^Nx{Tl%yq8NIapwzyo4`jZ9=}CWey5#dHt_)3nN2Xg^P+(99Iw zKu4!Ya2*w!lZ^*8M%|DjpD|`^V73w3P8r1tRIS$_v1El4T2vWQa)nXTG99<~;-*C&#~td8(a% zVJcGX=j(~u(qa8Da`}7~1~g){{F$g_9XQ<#gO-PdDV|Sv`+BAq!sO+U!}!qx$6Lhr zCOqE*eB21_&O3WZj54_uVu}`!9zk%3M;xbXT z>+QsCZ2W!Er#n_x4FNgIp;HRoFwk9pqO|4myA55v5L|2mVW#j3@piUfx>_n=H8kAf zsbU8R{LwVZ!N!VO#J=6g7%ic6!A-M!CoWJE)sx6h1g@J%T?#jx4InM@O|TB4UCIU+ z!hB%Yb4Lfj2*Q8;wkHkph$!6KY28q#qu1B$lz}in(Q*R=(V8s*5gSQdSTpF$^5Gp$L*roW*1wLEZHh=YiybGR@WYUN2G%VF@<^k7wx8fZaqq zN^Oc{DS!ZCYO|hDOTmM&+~%Y)!c_%*SA%Q63I6-1*mI4~={dcsZY%WIIu4`1g(3+h zSsmj9kiV?)W-In_^Aa0>p!NrKDqtj{e`AC&W6_a^upaB;pZzPN97)@P|_Fw!{x#ni+OYRs*IL5jZE2MMz}a0%Y`$@hxRPZ{fD`l~t{`k%=FVZb_Rj zo#syYxt%u1=t+I(I?PMiJ%xSt7kp6PsaFsDF-2_<&2{AmAv5NGinfkMa`{$Iz7?-~ zAz!`DRQwc$hkts=0*mXY0K6J@Ly*X3;Uqzun6aIhK7Ndt ze~=e`4C}Z&(?(Z+jcUT6&0VS}BQmyEAY!Gj#qA(>Jmv=J_&34u^hsJZQP9jcRC^x= zthSI7zQCXar)wF<7#^Lxy2-nhyYZd@Z_weHi?ghjk!r- z!8T6qPkM1|i%?uU0NS0K{c$vN00jgx{3vFq8i?+y59@Y+uj(2qf=}H@kf#{=Mt+&e zb$3%byLH@%zqQr7#l-e4!Hds#FQ+P3-JooFR@LlY)*=IP1h81Fn653d??giH;fSDr zzD69oeBkI9^4&`fwZ~JQu=m{A+ZI-Z#V*S;N~MPt^_*hG_)|;JTMw-0l&AEC#_`+p zqs?rz&Qw}|MPbv*gJ{7zo|ial`mIhnTR=0uL934E@8ve}@T?gg{Q7kVMIXCl&mG={ zrLQrMrAbDp4AK{_j~qJTj^l_G$vP2D1eJN<9Ql%a?O6qKpPni`TVOSyI$ z+1z4rC=J-0736x&27Xm?Zf8RD6%tCLw^PcCoAq&j0`p&DF-%1^=#(ZG8CEF0Gv}?8 z^*L3jD|!%2zP+$d?mb2bWthsd7%C=|Wc5h;fse6pgB##+tp=%A{WO&(B8#Z+(f&J1 z*t@6e>O)SLR$4N)3Q=$*{cD0JR;(d_icK6Re;m)dm>?^V2lDyJq!8*Yx@!L7nDq}#$3A;( zAAokGb!@n2-B%&@@h(uZC%(DhdxMhN;jGYq6>{s6;o(#c8k5n6v93Dg>K>z0Ph$32 zcje9Fa=+LrA*E{yLAWJ+#tFh;qLfiz>b8ruIM|t{!X8JfKmNRXRi~gfG)m+nf9w~3 zVw|B!%=YO?1xKdmoY&KM#kzGrKWR()5qe3p`a4`o!faAXP@WSgB?p3rlQMnuI&5}- zW+~1_|G*E2YdGzsX$-XH_(11D11y2yP&YhYr5Y!H$ef}&+R4v1YEoQM6sy^s&PUhw$mH>0V7Nxw zkVi!^9#`VMI!#sHdb~|HF#jY9G4zt4bnvqu4jYP4|&Xg%m@~( z)Ac;zfz9Gze;+#%xgzth8MKWFL8<733WaO~*=E}u`9R<#?RU2R#H}}3B27nwF?+li z3ulq(GVs^w*KjxPwi%M>I^G^mDQqgv@2|fyGTWJvMyNHkPdz|A2nm-<1Ib53>Anx4 z{)l=8pAqYSf2`G+^wb3jhO#4n(*&_oS)xWzz#J+$4{6AJXMvki4Rtpzf}y@uSs}h5 zUa>JEp^Q-ShW2FwFk4wq+wNJ=_hp3k1Q&@%v8i7o1qWyebuk_hOiyVKydF&c zxghI-e0Ib2YS;F~K=Jix;}H_JF#!28`_1%T9|XVs(rn}{V!nmYtCXdG%*ekrn&zKR zODUf)kA2Zlgb1q$t{M0i%p}{*saAy$PqF`OfR3sF*%JRyHqx3i#*c5z5Ep~?ApO-2 z=rQVb-gjmD!PmTwLqK?EYsVfF+y3q&2^(#_@pCJ?1s!CP!ue-%q0fN?u5@ga7S#`Y zuD*7e4B)}9FhWqCGjij9GrJm^dPpEFP}(4-0TKuxBbeS;YJZ@&-A4ogr;PN-YlQDk zV$)~qXLMfw!GKt{J8W2zwY@U7loa@jdKGr4O;N2XW|nStjQIHYw#F?aO#A>l3A@)O zv9#+#LlPx%2XKGQ6P@*Sv4JBX8tKzFPB!xx=p_QRcB6BXv}#;`M{zsp=myrEDX(<< ziPZ!)*Q`~zXFeh&>Zq$o@LC2(mzGsp0pywiUT6am*daRx=XxjmWzj_s2Z98U?v{y4 zsj&macMPL&xz33>F~;7IpNz$?hvxPe%U1G3lQ58RoquTdKu^H!vGu?Lb&9w=qG~4< zLirqyp&8J=$F)a)B9HaOf^59rFtQO;tZtiPSN26T{=-e*B=#bX2dW2HnwQ0;23Rjf zd+qJwW1V&3RY@k7$wgSD11xh+Y^@{Z)9Z4SFS@XqIZtEd|8+5Z1n{()ClJ^*Z{R;* zZ-P|Bxsfx#gq?MR0GWa}xxd)hj%9h-UYap81d9)tNPWG3l+Qsnf_VFx$(1%>J+ozZ zZSH}QF?iuKA7g%bX)hFTHx?##+^a4y}?bf&#-7=}F2@_Iv8h>YF6`>2g1n#Y=8aAqbT9 zY~p}7KYDoUd5QYRT|CYNpH7jAFl&|>G@{wE7a!l$ey|96U?9iSOR=6uNGK^OL)&b$ zMtYIs+8x+H6S^!HxUu77kT_G%R9jTR^I^Qw{Q7!)0I&)^QCS@n z;Yc012cjN0O@~Oc`9^|1rjBT%pxKJqDA&h-!lT{r68_X`%;1b0fY~ExdPL1d11JP0 z>-|_G@PPVTdy5xdw-tS7ijILA_?5j*bwcBc-w^jXUvv8heT+1;4H>^9nm5=0IjW72 zPL**?fe0#@qov7%^Ki^DDHA~G!lFPemA6(JYychuy~ivsHannG0&_53o1C6Ejb4g> zgGQ;>gYvCr>am@&l6NWnV^iOW`bW0!WO(c4Sy3IKn6KG13cpL9!{arTgZH8u$21aq zH0RP6Fb}(Z=XUt^x4#=sO3UXLe&Oddr@jc+nsVVBvEQ)5p41 zX*H_eY_KvW$DTa3K19%q9%;R?W*E~u=#l0Qs?{iS%R2e20&KXsNM*`@lGwB_ z7qM33AnRf@~7U*8#9`%M58l@ds{0=_60W+QLPnLCciVyA!1hErF?-=V4TuLg!Bn z1+)yBB{=WpjxN5brUbej5%7R-Iegq4JKDZ0=giK7d`AShCo&VB(X)1-~`?a3CNRxT1i<2%;nUB`cro50Vq+W@-y#H4|sv5 z2xH zHU`jOqMnwfzQS+D9cgSl&|dJ)z?Qgw=qgX!=mX@suDKU&&t_|Xj~V%31QhL)_9*>@ zO_UXZAzcC-(9Gd!n8%`VG9tjDU32*nnXYzZq7{-OL|<>vJh8y0n#{1O)i+kGtVum6 zYrcAz#)|4qSplT7{aokZPoe&?kw*VmwfT=@cVb{u002M$Nkl&|fa;O#eZlM4J!`dSjCqaytDPyb~2;SWBvYZ*Dkd^65!bL)squRVI> zm%sW!_|tFwx!(0v7C_>ew6~wl%~~HL!z(v#TaXT)-+FC-b7RKZ4sd`_`CxpC@f>%e&+}6%9|z#4n@1r~j>&gMqjqR_s`21vP0u8(v0!b`ECWswPK$zu2{hH4Xj)By-$P!knbJxj}%*gn>-O zo{i~*J0Op*Lx=%5{X$N45mEp&h@!`6H4sLC1-_tv@GUZ{=zW;Cm_~y{s!|CD@^!V+ zZj!kqCTjQ?x_MV%RM(uG^=~*+FEzhXu0zOwUO8|6vxZLen+YcY7xu~@6wnm&D^VMV z^|^R<$ON}s%?-vGkvfUk*(&e@5NUdPM#9jTjX8Yb!5{ikwOEWGE^NRAM?eJxOz&r; z>A$6a(N=sQgl~j>1b5~Mgm?sd93um+HA%}X*W4Hl5WpD#xI#of_~i0+dk%f6z7eer zX?F?m!><)lE2}f+j|g~UdR9+Yr)d#Ad^Blh_S847M^$A4jAZLUy}cf~mO46cVLZ`% z1flV?#C#;Wp{SVs(ng|RHVAAZTH4DZ`axoU##SK!No^`10Q0~H0u9+TsMkgi?F;bb z=1PfH7BB_oj0u0pqZ$BEbu{2peSx-g-mQyo3{vYKIcZk+Oj?$&;;CjSbEZw8h4Vuv zvrei_Gf)AF2mElku^C z9+j2;#xt|^$kWeX=pVW#+s`xo!@+s_xo-p*0H?~^GkJ!Ha|vIo#McBY(>UL&>C?Si zZE6vn8I!|)X;%^4WRAcYY7e5V=9}|>CLwsBf zU;ffX8}ERe4t~*g^{U&F`ZWBdEv!wI19b+#f&e(JEyN!-()f;fBtbI@nX4y#ZQcBc zSthT@A3(l$@^_sf3&Q1NGat&$sAzbPG!oH*|nz^vYyy~51I0{ zcmpL1+e8>*aRV^5m*l`#{M@~K-s_e(8aP8Ij)#Vp)-Wh+y=xsVkRA#ix5~!I=&;VwBv430CBU?wA;!$@fzK`FgsiS!i zO%ohIx3`wyeV-fmh|rLKxpRM5^{=;p21LVaR`3seG7<=+T6MwaS#H~y>0+Kb(vARR z-uBs}8h|u}=zRTy>TOEX_2g5f_A?VljmKd{dBr~$4$3*A;lua`tU%inKm;Jxsl!a_ zGn0a!jE(?1XgV>Ej0-%Y{V5}4^#JI~w0)`d$o7F^W1v9)uJ@tn;r_J9nr^E~p-(sWk zFVrG!Bfuel7o5ls;riOmd*LhZT-?m^a@vZP*bo<9{nzk1so&t6rHFP=SR z*W8j>9^)1us8OH@a1LfiXzw%%s77F4mHC%!wL*=?E_0`Axp?{gWoY9vBZPVyz!%^a z4zJ16n#aeko(A~z=J{UJ$chV%&JkmJD=iWHf1Ou<)XqoJGP!p}Be69v!9#CGdqaVHge&l?yH1Z%%6~T^- zYy8Q7RSSfiepaDqqjP3qztQ?~qO7Z$_iqWb zYLRv@PUA}<%(lP!)i0~wyy+7HD98U6{R8vX7-U`)a1Z!~-~>6ZlRK04;Jfn={xEhs z^q$!o`0`xRW6FV)1Eq2R?Ni?8%stFtDWx1Jg#)QfSqhi-=bHVJDPJ4#%NHOlK4JlX zWb5W!2!HZl{(Sg<9)@uKKmR|&Z+v}geOiBpKXdD6q*rW*1E{BBddLn>LzA3R>xDNr zHy@@X-+>YzHkEN*s zxX~n#?7U3pTxi-(Jzj-tolgg)E|124$4wLPo%3g`NPY6#lPrW@EFtaLB=8e~4PgY} z3+*2q-?OWark90YmmmeWN?m(pUIU0xjL3I%^r=AhWoaKAl^K;B-_OpXJV+4Knk=G%AgM82}Q% z62_d5J-^n+p8w{Wm~fPj<9PyFj89HUD4sW=byi1wzIDj28)nfbdVAvAOwh$#6Nl92 zh6XpK#Xr2w6Q6I11_%8HftLAbL48F*V@@RtKUkahz49=o@Q5UD&dU#rH{kc9j zjpwfl03(>jvl*Fe3;4^Y;poqQ+RGyV=IWYESyd(oZ6kBo9@X3H>h<6{KgV~-(+bt! z)44Xjm-BI&rzarIn)>3RfpexAB(Q{tyYxQ-1wb8g$PS3LTS5a4^JWLn;xc;Pxn+&7 zDVE0lUPq4ceFf7Z;L5Kl={_zN6)R7nG|YP~>YC;_`lu5DL?$&hk4htdD<%MIqAOy) zTWbFWbQ&3(5a`@!THbKeN-@C(vls+({CD_bl^3X%!`Zwj32F6OR3o6{ z(3S=7F@?K_esJlsMb(P|4oi3mf7lggZI8b&|->~eVf z!Wq-XP|Iz)7gNYP_l5;$>SE$iyKELpU5nztd>~Cddu~qfagdH&l&kSzHy!dnm@PR0@Bu&&8I6dV zv;@K;bhNYG07dVALyT1-=ixtxz8lYjE`-yEfIj`aJhpzS7D(s!yqeX5OXC zH%(*g4Vh{tfJy$lX)kt`0h`=eI4|oZfhkk-Gl~c}Z@`;@Kx$w>khmVqAukp#YD z{zh*tSPxx)Q%e8X)OV=AKKc4imah@dov+!n9rBzyQ4jK=&+;Cfmv5}$#}hWj-52Mq zz;J-Bft*O&m=`{g_7{;8n|fc=&?Wy|g0}#y4br%zJt5~Szw0!|;8>o>ouyE{J1Eq6-c^ylT;gQjzn^~pjm|poha3GZ_i?{NtC{w;x;8*eH zEuMM=K~)Q#l=hYbEP^pD!#~bZg%i&g74PxblsQAL(IVR%(PxT;&{`v*F%dK*bfaY= zFT-X!Zp(LPV0!hOkXK3taqw;J;>7k=|I$B7kD<1?W)o41R-fpfX(i$d__o+?M{4jS#pgnVPpwOju9xaf6 zd6{hD2%Bf7m_x%@J^j^OolMDQ#T<5GIv= zFLej_fzUYd`$z{Q0!(`%ts20?GxAl}BJ(@IsJ$dJ0hJc)hnKDj*p+Fp#t1~4V0sD> z6_>JZs)y>r+`7hGs%!G5(e6rg&>%a1FLR$hELD&DH z&2aCrqG@S!w@v<@j%31j7sw(#y#o#)z&)4m4Fp4N~ zCgh7SRi$hA?rA)8QBeQ^&W=iOF9rBol-URMxOC-)Ia>bOSH2|qE)k5qOyxKNF0iwV zu#D>W_~cWCY}Zb20BvAlK zk4!l+|60|U>C<=0mrv7Sr$rl)DeC0(GdX9M?<+i*|Tt0(c&63PCBGL`c%Gu+ScX!DfhZKf3!Q- zkIA%RZc%+=S%R}(2&xwI6eeFp1!WEboMFzO%(?Ri+DnX2pIew$>{`{yujMx{XcO!O zE-+6~XU_AfVy@FU(re^zicZ#?>01k$n=mznn4k87I%GD8@4D${a|ZP3@is?Ow%oFA z)FS~iea=RE&g;Q{eGqAEY8Ze92BwVp4UYg<028>DtCRkbq&bjfyv(FIU&*6I0H?K= zOk!$lnlsvg#rq@WDU~SEtorSe`a`YEKrwywdhlL}%Ss(7e_U3plj>#f9+fxlA932I z{-OT1rgsYIxjcuj>pD*wZjdUj2Ur4L=cMpEkNqC554j^Iwrr5v@?8XtP%)v@ZN^}ypfQ8D~SU* zx@8x>seS_tXlQ6KUtS0R)!Lo^@UtssQa})a83`7DEFS9wMsT#nz80Klm$2wXSTa+j zwM3vHtT++4@cZ*nW@|XeJtwEtL}i2UKZK7D%o6Y!lGc4^v;Eg*^f& zY54yEFhgU8GV#(~QaRD^K_H=w2-En9!W`y{>$k0pn1!HWf^$x^JNAMS2h2&%S%<%J zI9zUjf)CE8a_7C<{AiEU(&8DH(1lO982rl38)wX|C-d_M4I}yt{dGYaKa2A3w3CQc zn1lh!5j}Elb#YH3J?7R6vm3Oz5X{hCYj10{e#v$CX~KjLfem5tp@7`Mfqq?AzDW&0 z;Ni%)`iO*oMW6$KC`N#_r}`UgtdB{f;<+?`H!5`>V=i}0E(;LC-&LXh_f(q~_*TO& z->^W?hZ>J>o*h!)f@ca=k-VE)Ri5L0J*s`M;276$-xGffM=DlKnZL?MROPI8m)FC6 z0rJ!j--FC41k0ejNadVkPFmLm%m?Pz%;y&U&YfF4BBX;{%qQe+^N!DTx}W>VH>taS zfHr}c?QPBBqbJNS-0Rha|dn5IA77+_=dEgb0eqz_4oc(?@IxK znv-`+fQm;nk5Oj8Hrf^f+@lbj z@)PG?cs3vypWpb2^WnF@doBEbSAY2N3)dW^6DR-9zYc%%r&q$=C(By+EQIg;)~|;D zeM&E~U;Lf$-+uF?JYm;`^XK0T@0L$SJV@2wm$D#~e=1>tXpHAi4P~E+(`t5&Yb4Cw zl4--7{MeZ96kXAQBWW!}0hb)`;s_xa)JS4lU(cXPI&DIa7W4SuXp-Q6k2b_@X?oSy z*VqD>9pW~b2a*5k+M>NAS<0bz}(lUxhn)!v>6Mm?%$z6H4FeOQi|_^_)DuMR4;2mnQ!aJSSTun6IbI`4+2 z<@XB*`ak;KJDajeM0$FE^hO`sbiodO07wj-=rir48JQk_e(jd!@|Abrl$p_H^CNsj z5w%bd!w#O5{}!SE0$dZ34RE?jW2+Qu76`u9nM7`K!JYeZ$0Y4BA&{(6aSh^#jgKcL z!sQ!xG-tG#*(qZjzg$G{)F94+UkK?#isoIp`tI`?btVmPi0k=>f1(!6KeG#S7SRxp6OF!cnULoOTSzk8 z-Cu1LD1!?1t0gM~^BmR8H-8qahxlxknR(`H>YAO;;X`KbC4cG+5AyMqTIEaQd^3!nxamWm3e-hKDCW{0&8KSB*7P^VK7i+%G{Jb+5Z$&@t_B+Q+d(+`7?B}a3#9bw zasV*+{_u$W!EV+ze|2ko@M?xXJvO?>pHA6Yi}A_EP)e^Z2U3|bl_|H=yUPZCef;Y` zAKuMqdi~CScdv(k^WmLvWn|LI@mcv0UYrRau=BpN@p*K^K!}>z?Kr89o=@XkkmO|I&JCFGx{SB=JA^}d%&e&xh zaqfwKv;+ZYk_eUga@hVkwBPs`2_QPIF@eiQOx#=OZ!Lb>V2?FZJE(I}*m zsxkSD%9LByxVx2_R){}-f4+G+A7A}?Gypsuux?gkaa!6qT^&0&LBqEon;ZDVz_hl1 zLE23`k5AlJ^Oe=hl`qZ9nJ^gU9Kx>mVnKwQ)0F3Ce<1*bQ zNJ_VSpItn2Dtp^O2#Av{O!{R{IKZ%BeT&9&N-vKC0RKH*odTg-!?oM@!;QQ5<+9?$ z%PUa&Xhj7Vry=FMzM#>~74RDm*%33Z!$3Hgeh3MA(2gVx$7C<pNlMuP-U{O^rO_#BlM z7<>lsL(8X0!Y-O;jJpa2slXT0v;Z;ue8oa(p)!?n9v}s+pVf7N+*)w^*e`bUe7!#W zj=#tE7E1H$;W1^H6!=Ad-@(@rS}kaQ;q;#VUT)$U4GnXq`GpxU{G~j6AjjjXw6aDzPaUK1< zAGy!J=N?Ie{Cw{BIN!Za%g1xfZacJjG_44(IDYR}kPL{3u52=YIo-H6cf9!SdjI`*!|(mxAB3lh z@Y~VeY39wv3o4*FvyS0i$ehkx#k|A(3J{3-X(2GKc;0F1;p9Mt`~kOU^D~(X4`&@- zol>I(_SV_c(geSM9j@NKYoK?rIpWo+`Ri8`pNk9CJe4U6apvHh-!GYR*WedEzTDI? z7x+ROjOqXVfAz=4SwJwZx%}(juzUW+cdi`@A7BTkTfBZH!7QnR{{yqe)Ht% z6aaNZX0K>0eW>U_XB6cz4u}xnTaNGagE>7P$NCde$7g5QeNl9Dv`QOc-hc~CXCKLD z6Iwe%13MFc%;WNoj!()DQQ}wc&ixUY!aXsqi3K^Src4m#0dJo>6IMcrqrFc?17D z!=q#7;Ga2RHw3Usze4bLVyxpRupILTdD`5fJQF{G8)n&zJDle)>xii}eFkUR^y|GO z>dfahK!n;Xrb1=q8qz4U&C6TN*|eR{EAf5Qw-8@>MxivHvuPVl;ZO8QTZxSjX=LJ$ zkA7c&Dt&(aR+vzKY}Cd^y};Mlw2-nwyq9)cR zrg;Gq@HbAMqzD01V*u|r&JBeVGBNt}(x>6WAATHu@~3`6?>hw<(ER(=uYTFC-RYm1 z`8fmSkI0J`0+V6Um3=K6$Gb7a|ys#J{naWy9;QIgOj%BDmOrokk|}pRJO9?#&Rgvs#xNv`>yKe`is_5Gp1y$4_Wt3LdH z${(+XUx_P~e&2Tv5It>GLh*Wy7W4WJxe#Rzdgb2)A2CiznxZ#K=*Hg)!U;ky3uO}` zGC~2u0oNdCafDb%$9|mt5XO`Ka>hFq!V00waM=Oc`j z-yq4KxIahuhKlN8Ahfn{f)4nFZ@J}vjL=RU&MOLIAwMzYs2ly}_T6FA#6fEYL2#hA zJDWAyUu|a-zycTpu&2IZ!*@{j!X11$B|ld5|4Y{tWpsHp49W)>zOZooj`jv7i7eJ3 zGds>aV<@Ag(JnJVh;rQ($c5=pJWfDSY= zTstI_{ieg{lP4-et(9d=KpcJQ?BGV2azKVT0dUhYT?I&HOxie7-uRtDLkul208Rj9 z#+i+2@tx=M8^_~-VkVSGGYZW%01TY`7g`7tq4kM`e0Q*&H6h(pdPV;gj{pcOl|TIo zO)wSFXw&?mvM_&89%dVO??fMeNy@5YrmWPFYpwjI`L>Zp{Sr#%6kTL_@Ab|ZAT5Cb zPAE_Sb1zXsYt6}21a9S&byGcTex*$|=4?+-=HASlE&efQJ2AhyGj`7UJ)|?Y!dIta zJZIj;ymwq+EB+$^T}PP+9gPvbqADXZVR!SkfAu`uoFJYpa| zX42i#pqh}zDp7=~UW0rF;(Km!X(3#>axMJA-}{Cl9&Y&5^L%%5WbVFk=e~mE)LXBh zO_&R4j|&J>Cs(d3a$`d{qo69ZFf`De z%vnT{#iwYu{63}hdU1exd|vN@*3SF@>g&aggH+p!008zkAAN4YNiGVc_rNy?sg!A1 z_J;$hOu0XN-tJTOOQzhl)QSlsgpV&MtL^o>e;(e;XnbutuXF!@Qc)iPzkWJ%uicpb zNjXr21LoLPi`}J_Wm~Z8s{u`j537r0NHMIw>R19q2OV77o~MQzJE6oaoyxkDOV!OaCw16=8MB}lqyG?+XA{DAUJrNH&+Y0+VinS9Fb{7^N zSvgrm_UJc+Z?6_Tr&8mhb`Aee(o#|9U>xwF&wu#&6$ABu_=#1 zCdt%&RN85y^38+)Gfb7P%XA8DIf6=Xzr7$e?f?*Y4*jrG8c*}{OJ>r_Jir*=JSG(# zPxtzGWy}JC{O$PX;k~!dn*dLFVp@Hv^Eke8gXUjv`m@BFTB9g{$>frzdeZ%KEuZK=$fj0aMt}lf!GCC1P z*2gWdhOY*S3;srz=xpQ_rpRa2S za-YrT@Kt~={g`$JG>m!f{o_TRCqO9ug%5Kzb2&hN#YdN}O4G1kCds`TwwRPA?U=k@YE%t>_au2|${M|UC@1BO%UVkR*j$en`3monch5$e z^>dXy<||`yJMT&7c`}aGA5~{Tt>$A6P0LBmG3(0no&sCY_CNl?JJv>j`-2~Z4n?*t z7w~m|_0}ClN_--1Sw(Peu2&iKPvdM(;B;R%j(&Hyl;xcu{p`@JIqc?*TjAu1lQjDa zbn)zIn_uV~{R$}Y;ib#w*d8s&m~wx|Y3cV@n*-K2nE+mKVxxlhFYkKB&S45mg@ReIo@Zrb4ttgu+#6uRzr@Z$kL6m#+jNfGHOC2m!p5 zJ*`rvlgo0x4FFbInFv21$MV-JX z{W~}GqCj{3j(lJY4#+fcLjx(dZ1Hb@G@mY<9JFoyHS_Fn|JM zD*b4Tut6RyQg1xrH<|PXJMv>!9qMq$UIqz@hisZ=ALB%s23^ob*6GIh%aA&*yB~jebL(%rQy6$tFal%t@tJ^}>7(Um>^> z^A#UV4zP(CY9q>6VP>-O45zs{;rWarc4EePM<4oGtF#p{seLl}%-R<1KZ2X^a^aqA zS)+O+Wlcae#z3KWPZrdqyy^oLCH9YvdL-a!F`ko#rWTa1tXsb)y-n1A-}LTmmmecE zo9+pyUcd!`^8M1q^Wig@5;3+rwV}iKg=RG;oXq1~njjdZ&NY^i{`}z6&!urUuePW& z@S8TFKD@KhK#OMo=~u`|c@q(KNG^AcDuUAbY4eDWs(*qi50nK{$2 zguPV>OhjqqS|?um{(HTW{qW=;0&J^%;Sp&Lzzl)+SZBL5ZC(w)%j?e(;1B_8LFPjZ z4Zr~hd-2eq(DwL$dqRV-)SW)+*L|!@!g8JYwCV3XDnBERRtM;SFEPk~HRXeFj{tnU@0j(&>1VTHbo7aw+T(9U8Y-#>W8cSm0xYSB%7gi! zqE2Oz`5Hd5s0$!O7F@6=C4Ww8-odQ%>D2QuBccB@ft2_)LKr7+@ADH+r=>a4WTsy} z_b^ATX-cwxc}H^u*Hb3up4>F9d7-TFe5QL75J>z{(c-y|JR!7^*wF+$#TOqDGmC+) zcGI^0C^(4n#P=A1NNNZoV4D(XbS3lYmi5TY%ha{doE@hb@1i-|=5yr%YvQyl4{~_U zc|OKFo~bF^?f`s1gTKqX8@#PrTAB>BBzVG#`XDumcE8`3 zTUOZVW#L%p-D7DvWhE~!>kIuOs>eoOF3$4=_!F;6;CXIY-FGGRNwh21%}?I&qtWnV zAG~d5zVP(q@xD!F!@R8R+S@@x>+@n*(eRYD&;| zX@MVbryfLtGj}1DpMZJcHS?Ue{$F0*C!)Wrv3FsZ+qhNOPXE) zU;tmxV0%;eU;pDD2}A$#53-!*T8Mj7N;yy}2U-SkSu(i3^2sUYv75 z0-*w-r4XTYQl-VT4xIaYjtRt^%=8g__pBNi zf_Bh$37CXMAER49lz- z(Y;mvvKU+NlL2H}SxO-g1$_%uF(1d&hx{4)fMNI_`~1o+)wkM&V>Ch-e}EhKbnyE) zLfi*=8PNnBQE-7p39t;`^QQ(aJ^n)t24(hGBcO0nk>ZvljN^Zd@&k%C3TUiXJF=5# ze;PKKXOIWa&DC__*~QYl%wATzSbx0CUVlH&(dQ&U5kT5oGSO?+&NhJq%1LwTv-cF$ zvrgJKn4r=2RxUjRZCa;dW!-5Sd2_GdzZ1TaN1^*jqfc1?lnjt(hfIGW9j!a>OI{9+ zxoyR@kfr5$_i*0tFI1jFeP=uU1HhTGfBCblJW>DjGG_CP%37f^0v@sP)1+}il*BcG zzykt<5fmNp$@Q04C!ncKX4IGh$J8od7{xDqn>{58=?RT=b;N$T9e|Ij3 zpO}*h{MEa``H;OR{gQIvHR1s8!kO6xf#Hkt_t&6^(NDDw84quq9W;RWFtDOZ8msv1 z+^M$xVW?wT#uqt|%9Jnijj|sy<*vc6zqmbS;MYI?U`WpEClkI20GrmQO*`;V zM(#+`^!J~A?VNq*-jq@fe>|W7!emT{ucb#$^2Iwx$QS#3FO=rjat+{E zWlbU&lSPGu#yJVr54Fo(Ev>V)Y7Izv1CI0MzPJpOt5%C)z%KI-B@-+J<9ymA-1{n! z30D$=JLNBJa(W}$-9fG!rhCs(|JWv>A>})T+=}MWRcQt+%b1NJ_?OH0FwpPv$jIMlce3{ zLT#-a-lMrxFQ6GAc~;{Oe`nerjWr&xKJFSD>+JdKGKFKDd0rGfem>Xp&pVf8jD>v?%Mh_t3QTYvVpHpc0#N_|&{4}N+|A)bjw-_5%Z!x6pHnIG85fOrm3ZZX%Sxfi9;MG%LC zD#Dv@y%}y?zqtwgB5fy|9hpqiL&||yjsv)mSQ}VZROl)5orrr8qPwCozgu5?`^78* z|6nSg(yPmXRHi%}ZM$DG<>r<84tVn~zH`m)`X@heF1SV)e!72^=j= zrYdn7dFD&=Ygh>by5ZEgSz1CY{1L#N{9cX_$D|zi1WU9{9*;c@EfNx5W#Egt(+mhR z_-q0II4tyBf2NPSB|x-xfqtJ|zh(X|w#pA-3vdO;*$`k)mHG>2G(<5&Yp3_9%qt_% zWve{nbO5t6Cy$#yxkhO+0@M+i56AcKy>-FNc)1>vL;5U0hDm*yOaM(h6g@;z7kCbvGN{=R*G*nH9v1O!m6Qud0J0kdu>w>-tlO1X0Thu1Ni zrhMZ(c}Bi8zlJ0A=M$gvd}F?PUb$(U_j^g>ldT89X{PV^eH?f4ekn&;<1+a7Y*|$< zFRLn%{k~nE^PZj6JUJz>{HDhGqRbG{CVehYf13VXi~#oYMFD19w`uGYBm8@f$!wC~ z0?ZG5oXiwceGgEqR6+wAP@TPF;ui7J5#Ot``;-O*;lh z(f|Z!Pn|R$KBE(pCREaKS>PwmtBE>Of2ne^Tu+1~j!9&#Buz?Ng0wPzU$mG;XhV?a zKF`aA2+~}rL#TCO#RLj1`pFk(%&f_?KEEVnp!KmVz%W_(SILBJa#KV-%JeFd3*Bf0 z92TmPc2PNq2#%KomSCcTFB>!MDF7a!A%%lg`W@g8{uA;#M}(+K{HR*6F&Mp@xjsDoCvw(&Y0vmm{#Hs$;PYhN2tf_ z5JKw>AkDxqnPx%o8~e`HK^KYqRd4Jexn+U!^_scv^=F4dtjDaG>fKc`Rh zg$H93;fb_Hs@F4hR{`7bb0S_a{xJ(9Psn_fW%$Eb@%f`#^A+=p12&Yy)0kT*r<0Xc zdHTHbLYngRe3+BWFPG}kB(UMx+^qRD^SpL)B)ABDmOh2QFSPSE$|^r&e|o01hw5Bf zSvSwwlqqQs>ao2!n|n9rYyq+QU6AKuDO!rot71I&e7Tl(tx&&n+N!MadntbRc%I3- zNA0f@`O3;O#d7tJ;$__p&$$=cG$wgZX2z*ryVBI^k@n!E%^~*%bYD0#nE2AOJB$5E zfaw>nKtr?evJ>;{F+DFHf1{oUbn!`fdOfAGG~m) zSK^!^Jugb*9Mi_>XVc-n0*Czk&;P7nUh)y&ifjgLr5#sthUtYV2M#y~=<|=I&5m|u zvx3NkP&zn$aH^KPuMbXn4u45fnKIdY+E}0PPETdZt$H@=AJ!*(e<@QIUWIH`x+wiO z{_vyl^;5^p{PFUyeImrdUke=j&tw`2{odtanMi&V<*@%>KltE- zJ$vZ4zV)p=%X=>~o|Y-gj07dj9L~LU-h6Fj*2jDK*iCsq8Xq^!7&IynLRko}NK=LA zb!f^UG)5L_YrA1QdX; zj6lu3`h)`k7389tfzYw}oiZ z7b|s6AXg^(TD^ojd|R;_NBhNu_80;nKBF*EiiJ!xp@v5QcR+~0$E^;42KW`DK1AMg zFeVYCOCOUCn&8|z0ltFxcqb55?PzDGjUyXpfK+-GX2ea>$OIHOFk6MkVfBEV-Y3H5 zy0qOE!xQmge?cak0AXlgac_g#ykDASEYi{NO3pLZlX1>-@F9lAC#I0k^o;L)cqyDe zJt$x&sx{6!LQ>AfAa9f_2*`ChRHd+gSKkG$byzB$|5QWqwmQa+yRSuxLxmvt+i zC+`8;E-q`{caQoRe6v20X>KJ1CzD$`Tk%o`N=1rhW_Jy^08LhGE+Un-u-}4XjalLrs<{f zB=2IPHCKjdnb!l79qTA9fJ~$2LZ@6H+cmvDx_s4w8325#cZWaulRpdR&YhJ{KR1K) z9JW4UVu@zjnP|gd>$}n`QVtwm4&e9hEk!~0e|Ny)tw~z)a&jP*DU;1W-s4d69?Pr& z3%u7;nR0(*%F*}zwBnO^p9Op{_E^JSH1 zO`(dL74@t`A3tcNpQ`j7<`M<7zYk+#OLond~9Zv_2M5UO$ht+ ze>8QL=?Iyzl^u8bU2IG;<`}QglE(ete0)h`t42}Y7R`4V4(quFO3aCa0s(Xge>7|~ ztt-a-qJXY$Y2R&VSyZUrY72p6b4sfbh%kXBiOP$~A~ew7Z9JLRb3EcFpgJW<7VpV! z9e#z#|MP3NY;Is)APVd`fnk_$4G#1RNSg@r0@lz-p$?S-Lu&;JCg7KZcjgx5?Q;u9 zpR*A1h5v7uOhnWL2&c)?qe-6=YS#ZVEmNN4TX}LT{JB#xR0$s<(pPK2~8)t?h zKJm2DiFxgU>U~WD?s*j^VO4%!< zorntr!2Dd`$R1kY5AW?2^%EklmKyYpK8Ei@qP7wU1OU;>sv|`GaX+wBAwPq9=a!R> z%P-!P%){rkG4hvx@t5YXfB(mR^hbB}2(*9eH3y`Wa^Q9102^*89SjbnGGz#i-1h-0ruZ-3}p3BO93IX)mt$U`)f6<@?b4U9T304WX zgaB?plosb}b=vvQ{ANd5a!>|@O6r5q%5sxyAOs~evP^i;cV#I-J;`s)oMR^-RU%L& z=2$$7XCe$Ba6t%hD{G0S0BLAcaF6ZmQ-}%)7!oe(C2TwJ#eM83k55h-Sa1-4P0F+v z1fLK#F^4%!6T*s;(pjXa$7mrD4?dh6`zO_$%5X5Xy99e^+TI+~e` zEz{7HaB5>!V*@j-?6})vOrjXccqJeFh2S#_6UG*S$beSKe|XbaW8A5qRaULr9J#cz zWPZ|!uDHO9Qe!liXiGpaC^xESt(@0mx_DK7%K%@P69FSA_s0C8=c$ee+h~C;$V?P) zOvb;sjigW$;ztoLD{wv4+_;I zE-Sgjbzkkk>!%AB+J^a5qZ4xnyjR0V=z;7xR0D(}6%E zfVN$mv^bOd0mdS_x^s!lw}3Ks5IbKs@YXq^RrO9`(QYn9Xwgu4JBNKIRNH@_^)`X z#VZR}7L0GdamGvv-n)4Igaj4;PVmr z>|}1?K6|#x;`5Q8^P}^e-w@aPF`g^ef0V~_Y4bEy*X;`UB5xvrVnz$tPrY*c0M~eV z0ye?M)P zuf|{H5#JZ{mGhp)^O(077mNwCCvgV9m4o`EwwwPhXg+I`>Ew2Za?y<)IHX+Oe?Pp8 zegq`zK1wt>?-|LH(7MI6IiYpa+C(dNKv5irN5(A>go9tKLwM$>0_)6cj5KQfbLERK z!Y}{*|Ih&I3N+>flsmD(0kD=*%7K&vDF+TB2U3|bl_?KmzuZolGD~sWP~Ogc+r4(< z&A#0`)9ZK5foVliGR>@A=M=Pfe_IR>9z70^Mkg#!7O^at8|8c(ZHiAX-!M~x+()sS z`Nj3yp-UQ9ECktI?>SbQFOA&1O8x!u=hsb;MI(ap5t+;v_vFC>mxXw~?|v>m7P{8Y z`?dbt^UwY5=lvMp=jSk!WP$G5VF)I5@;THDz;VE-Tu#;FYSMq<4WC?M7}tF>9GQE? z;rUHn0Bl{OATIfYd|`d~>9btKtTlgRyj5Hq&J#U~OL2EA1&TWaFYeGni@QT{_ux{j z6o&vsQwkIc!HNfWcMa|Yg6sYM{#Rb^`##L>=bW>1c4jAihWvDggM{a;MLOz-oW}K3 zMyvy|V!9`{tXC394;E;CpdAQYH%(LJ8t3=0+jxsnD$km6=Msb@jSh6S?md5&17Iq- z@7G#IsPcjAmt`3)UW{VA%5Kh)^@bA4aami_h|y|GfX1z7Gt*ojkm4M}XOy&IQp-OveRYVyF8&QxyX7pdb6ABs09F zP_ICcDPPYdenrS&ZU~y0K3Ub(svkMDh_bg7S7~uHzX>(N-rt+hjT?W9y)F`YW90oo zt#WG2aLW5(nkQ$RpMWuLozc?S?NvC3{HWI++x}p((&7oV%tdi=@nbNmMa=-AxV4`0s|MoCV+)RS-~To&7J8EC z`^UTgI8QXjj>?%erwIM>LGStcnKJmP12v(tO_t|Mq$!^3jxFS zv0&$EBOCR)y)f6RT=;Qs$CW2bb&l*tsdJk zPk%L{$EKm~;i`X{(O*=AilP??fR}>U?=a*l$$x7upBjVnRUu$3T&`r%*osSZX-*0A z+RS9>l!1==g9MLkF(ofej1oMRODY7_wt>UtI<3vu)A-F8bdQPgQ%iNS?uBeLqB~Og zuLnh&)=cA`BnGD234JvFBTzN!4wP5iiwr*pDKOE zL5y3zRyo7h0%Dn&A|mezkXMOVSa+lmS>p3yvNwwf(`H z1rYYCpGkGq6X9}X5x(Tm$t?}A!pYV41~#~Bs>%2%6AKcR@4g4!rK~~LpgN&6ZJV~2 zRpywm7lm;FB1Pfx4Mot5BLwYxrJznIP!)gvTLPB)hQd zy#0QAvg~@QaWrC)qqZ=X)!_L1CJvtDJh3`9R+kavZybT5#;@a|dFO~H=C%L4Wxf;H-!nm$!{=v*h)rHLqILW zMcS#IpA3BpJYOEr+WtqvJg}tSSuo&c)ysCra%uLwet;mxt=TnNTzingB`ksC)<@HIHPLd#}AlRzV5FNWfSwP4@%kR&nz0c(qmpNu{W z1f(A(f?57zM?=Eo+6#%R=r7`DznFaZCi|ASJ5wu+H=#tUTEH5#Pr}91T#|q2Bk*de z>^-2BgY3}V&{zb4L!NR9$KJM?ii<_(t1gFm+2L}9Wo#<{5U;v0^#(9_P_J|cwk+az z+uV&njSSwUxm9aL)r*i{PGM|RfL)_x{}{@FRvLmpg1lX6n4B+xzXrBBpu@#deF4k( zyP^hU=Gc0hRstS&Zz=S|*Sk4-IGk2bH90ovn%Fh80uR4|-Xz33 zrv5yRc(P&Qf+{0PY=Ah}+)5z*%vN0%vz({G6GiaOL#C(`SW zddX^#PsFenA%9dx2Sp-x@GgHRo&KG6bx`;#XaZ}`#aPtw_G_e;aJzq9$Kg0gj6$nG zoXjj3wj=;VYpSFIus$|I4n+oMErTBQKIPqo=>OI_K$o-6J~{EX)gb>D=&@RMW!=#K z&P86gZddke^IOe~(_G`<`A^NXCUI}MI37Es)|4W1nE9BkM|&0{B}%vhyx~MiLjt*` zrIFM0R%h%bk?01Mld^wchK6ZbdxI+A2h5~K8wcNNgbh_y^m$r-&>O)1wru@9{(673 zgD;UDc&TBsTTbeVqC>Edl={$TIk+aP1yQ;_fhBE1lsGinEGyKg+i`33j@D`hRmOy` zzDm8VaDo)y`lrd!PEa$s$R=?xkBNhZfHs_D({= z{bTawU#~ZzCeLl!G)_w-_~FRNOykE05#(av-{u>OBsAA$VO|Z*vEiK1Rmvjz%@q9> zsYxSOEu&tnwk4CIq~it{001C#Rk^#)sCklW=0@Jq4t=WU^F6lv-**vQN^$J_n6MTK zX-Oax?Q+w^x-EZ@FJ#Y+1!_lkbO}Kl+shT!^Ii8hT7KbzE>-v-u{D=js>Cu|;@@vg z^&B394?1Lz*T+&rr4>I){Bo!^C4R*Tu@qdLfBtqAKq{`vC(f3uWRTjTvrBp}G@QWxU?p zg;~({As~Oiqm6B+FF=gzK*H82njyDuv8UA$M}lB~=dQr>zK}&W zvhix(&_U^|Cp5Q*Rth!iKjzF`mIX+e298yZY)g&TycAS+BEE{?gGiKrCJhjratgeW zFDcQnW)PN#6V|^`y z*5{W=4bowZIq~#B?mNL&_lk|D)ue<^E%OT$FE0zgFA0$umLGmSI^v@C^puY^HGz$D zQ8VVA_!Y%{&WgFqCx0KGos7EhM7T!T(1OA`3;NI~OT5H!QIDbt+0>aARm;x<3jfz% z{9k{t)cdy?TTkugQd0WsOoR@ZHEGtEx8P4H&-8-+8XpODKG=sy?j)(Z<(cblKIfb3 zza1YLI>s*TU$r^VcvhT^;~p+1z& z4-%c14p8)P7Dp0Wpu2~Hz!ALBp@DX6q9oB=unZD)0zDp&W0t8 zEx*Bi`i|*8M-YYn#Gd{(rJuCOL?Y@~u}hMVAf*C6ugEAu|z#i=M{~guWB!qXsecZNG_+K;j|NPed zQ}gyrO+a(ye^8RLoh8o7>fg8*j*frz^C=LxOxGpZhBfG$t4@(*R^MmL!5{tzwV0)n zFWOb~f?@6J$U(uXd*A6Bb6MkLs?Rc#Esn}`D%on|$hFDk(u0zhz=p^Im zpC0|hk=HiqXUK%S4Ql_SeUsT`LhG@Vx@}`ym#_5KRm9$Nr~P_cvr&KQqZet}pXk_lk+K|TJx@3^^owwG~G0}B>)(#Vi z5mzGg>DQ=BJPnebgE;#}^z?+nWV0OTF9i@ml$1 z?FYm zyyPsWap*nlym_T$?98LHn8j_+K7qw`urZ3t!-uAgAJnJ;JYNu6-?;Z-*=kE{6Ns<8 zl1>^?-#&jjy5?dl%u3ZS&l(|nvXd{9EJXl1%LD#D99(WmuB8X-G0|GpC-DawLrYb~ z8nObNQ>Rwu=Gf~8wP=6*(xX4p>Z$}^eoD@=^?#qJ4&2v|4gXJ1DhI^qpQHGJHd0U$~8KOQJ@gL79jGvkhZQmMV(Us z@k+$z@oF@uo$K(Pur;lbQdozWsk-H@Uud=P_{{DubMh@3xbo)Xokzj%aozF#eA>cL z&5OO;{~wMRd;I$@w#RMh*g_rVOfi8@K+T5ESLu$eDK9TZ!g*7K@qIeO$TmpMoMj1=E zDNX|aO8l(U9uZfwh5IB%kC2M6@v=}mNsE;NEany;>}P+|`4IHuLE;bERxy=DB-&eb znSYOs!3m6;@~va2wn0*d5@$1j&ix|~6J3LN#GP}FBRy||AH8JZKtL#Op*Q`0z<*nj z(iT}Cxv7(+i_i>C_E#CXmFx|85Td(veCX?)!VH4^($-|Lklu4=<*GN<>atSCAesBb zz1jNi#HN3V98Zz85>Ft(nM31u*JiC9%$xE#H|aK5-9=f1VHc5X`V@40o?w`p!3=ZR zBZ(&&!GQFn=svkzCQZNF+f5RcrV52eGQ{Z1lgnI2+zag1- z>h9bq9i=~4M@|0=!kZM<3Z03K%OzPt6PBEjIkbPrJ4(R1n}R@@ZXplMqhP_Gq#38E z|7ktG-~|0(4@383B6wRWq%dqyIz*cmlC8Qv)}s>Z-VC&OPb*!k?0ei606`9C(g-ZX z59;FLTRZf*(~WC8HL1U-q{F10J@oOq7MD8nfU2#rYv6Q0lr}I&53E2leEp66_o8I? z3M_wf*o2)>lOjJ_fL2F88K~%y@5JT#{IA*eVCVW{`5PY2m2Im(cOw#VBIB17>p|4O z0fYK`j82K=5Aw&9^(v55v6j2)G<)cPxj1<)&4{RMbup+gyy(-6U3u`lR<bee2xtfZg-;ZNb3`d!7 z_&-NoN4{>|zM9?vceWGYoliYAvA`G3!urRnePbDk4Cy})QYto6{TVXv&arw-4$zSt zCUKLF-lfhr)_&r+uE0hPaJJ-N#bAF*#qLN~t|{T@bS2EHnyLR9{E0POe33M1! z@&BB0Mg_hfSP3t&SsoNT{N}-j%09kLS?50-#?-F>Wh0$1ZlL?(j;{tF*JFRorlOii zS4Cnys`n**_|V{MP!3JGrL)lV>uPU5d1xAm)#tdA^Fs3?3=XI^t<-Ht!}PJ<5@0Vh zlotR~XSh)&rw+*AZ1CGAeXCWYl*>HGe6b(n)yi{&F&QvT>;zg82JyX zA{J-0B)!&H;2lDT;-6@eTwsAKPC)epo^ay`oGCJ`wTtk9w}T>GHex*d1I;ZG}uOfJ?mKB-m~M z^lcSYuI4{0&>01F+Ngg__4Jt)3C5U}#@nUf7Iq_i(}9zmyP#rGERUOW4#x)xt^oah z?&qP-weJpOteJqh+s$XPu?allH|60Ze4;~=q%uERZ7-8VuAV*`0N!sU&j(a%|KjD* z_>vzw=GHUZ31f`ltn=KQ6@$dkEQEX(&C{cA(pHNHnTh{IWx;=H6pV>1lr0 zWlJMrDl8xr{a8rv&#WE4iDUln?o*U@_?~pU7YpCgL-n}s{C}oD4!pycA1QvtiZ?Qm&Z8O0)j;OVg;Rwr(Jd9SD zo?;m5OK~HmV=sT};?IvvN~444$)|??IyX|>Zx%!kqS!O!;eBP?fnQ`K($2CH{LbEI z2lbkCAhGb;{@6IX-cFEb)*+6wcw4L3`cs1QZm~8|_{n1VutN$2Yso^b9H3f__I1~e ztA+l2gZaQ6`Ncg@!oe+)_INtRN}?_4-;arHTt9R?uHk=_h`#1HMTZ0UYR^#n%e^0Z zxs{2Q1UXcUX3$K@P$J694Rg1K7gD4nK}&z>pa1un#URN;KYHx)0^0P>-E%{cZX?tb zSE1zLQ(CZ~LXn=0!g)=`O&0kB0Yh*8vhXstaeXE!ktoK*TaN^w`AQ)? zWg}S8!&^f0?Q{v@KJ5;AhBWPT>IV#p{y%(2!I3QG%%P}bMPJ;Yjs!w zyAywhjJGUpkYXR^J$sA6cy2XV8XGAo8 zC3_ItPOvjacE2=}(b3_=Kn(;T_?uvs+Z4uPCUa~-CMzk-hGcx9r7uhS08erk^RiE^ z7PU|av-ZW`YJd2D&YMQ?NpneZ*rWh1+A9Vekg9CvVldxludH(6tkTx|23CL<;(dSZ zK@>jN-RxQwygt?d+1&%J%?{=rr5<;ig^npkXSNtlf<^$R7kQocWV}w@oOIKXK3Yt9 zeeZC|fUD*$A{OJ8IqwM_F4oEWhjHZl{;-zS;v~9<6=K?u!aIZe)XS4d~(=kPTk24`zCo6-^YF=@)e?ne_UD; z0}x&Ta2cIrq_TgK2egTNLj;*zqdQD_scMzj?4I5!HYDY~7R6W?^jMrHKI4D;VRP6r zX(RFl6~C?)CFNQW=`#{NnWYP|!==zrRF!V75a@fKs=;%lJghXiu9G?G*F2Qz4M<4s z70J(ph@)yjg7&#C8>SL;?uG+fwH#|KK|6l(rHnuaE@L_n|8Ft~<RA)AR%9KU#tZp%li^|#14%SZ`$4(naC(AuWKfsJ}{ zo@;ZQtQwQKLkHuNu7tLmsk3SUc9*G&T<*(oUG7?o$oZn^VuDZHhi!mtSG2>cA*!h# zRE}2x*ptY^m9sMB)M-~=M@0Y-G)*E(7qo#Z*$08;#_U>ohHgcXpL2gfEj1fL_!7JF zho7P?`LhZWANEf(NZufY;ENe4qa5} z$3MUSexD|PP^EG~rnhxBprhX_Af3NQlY_c4ygBUGBD!jeO%} zrNPj2%*P~D!Q;S;mXv?w6W>Cx47y{LLRi+o=+gZ9&BZ${1c1sN^;n6m0Ya_){PFB> zBg7n_A*K>&Bjril5hTW2821FZke1hu4mPiM&tbz#y9|W;hnIrnuJ2-7JKiU#c1XQmy}y3VN@j95p1as$@@n@&N;|$`DkY5GS;{F`k&9# z514A~FocS&oN;UoROl%3v_P zJ*@QrY`r`51bcE@*4b&4@fto2zF>V@*J!F8P&Oy|%Tp`3q}_0^hq9}-Cu-ztGJ2E| z*J#|p1_ii$m7<;^p4LwMVn>kDH=B<2rw`V6I)T#na14Kqj-Cw*xFT{-PfYXTEuiw_ zu_|Shfh+0TD*XVb47KD)5xK_ZjWm0nBb!p+uQ+$IlPFbu)L75M7IUpu5})x7bqy?4 z7D`VUrCpetrv*~H+P_g!mBg%kF-mm8@Kmm#^jSu=PAI zUb}>{Q|uJ(Ht(oFw~`$DH;);%69k4no)=;lKA+I2Pou1Gq^Su$l0-It$0h>!=R%ty z|B~sKjk{;Tvp;4*jaMQ;Y=yC+uF>(WX?uf#6|8*OOmvwaU*tA~5rzs3EdCpY!`TYz zP9=Y|eR47qEQ2L$1d4_Ep_3Em>XAF;qhVCaYb)FFS(7(KusmYInlx>(oUcyKQo~`4 zt{?17>W^kLkAS08lMNV-Qhk3jCQ}CZSof#8X&?SJCWfX*S#74eeY4=5M)XZ}t3hm8 z_J*dPeoQ-$bvj@g(dc97f%iFuwWU8|7V&@1d~zKE!NvE~9(@xWx&4e{19&lG+%2^8 zAU%Y^RMkyl=^{=_lajEF_Yz=T+1&u*G`$exYvtk6PJu`FPF(~UYmA3d-PY-v3pbzJ zI<=^b>8LVR1~p4*V@$72OXo}kP>D^?^?O*sH4;kWB7Vo06MaczQ2#{L^y?0r4EBF{ zTab>C2TenAD;xJ8iE{4M-(%_RK{ruE_ls2Icz3J&(saoT5(Tpfr48Nq;?N@LSTvWF zZ{$=Zb@WG+{=WQSF)VFbnaPr-+{x5mmw7_Ay6x7w50A0vL`m_9gMJB*rUCxPz>7(r zg3F~{cHx&g%Oz;d>vpS*FwpGqF!_IV?Dd&Vv73U-YWc47r7sUw_>L{7KVk;QQKuqa#_Ze=gC~TPckU9^$;k6z)L-uFGUj85Q`a zDRZ{+<048Vh{p%P)}|A_I)_L{>kE|}k52>mRpe>umbQJ-bRK+fv!}~E+Sq?cx4kDM zG=Lw=zVIZN4{M)y`OGxt=YE6r%#;u^zd7)9ijr4GBQ>`u^};`OUy`q^X=F^9DZu}Yv6?_ zCrXF5nS?ReJ_6|mK*@Vq$60^)ro#ZViHTG6v4qmcqOnTt7gumF&1>bT`O@D zI|!Q5e{RX-KlDas5-O69&ktyx~OPT=@%ooP~@o8-(K%XC-(cOR?awS%3<1>ZOxdDa%vPghX9e|CI>| z+6|BPBk#iXbDJ_ghI|{tIPl{c(<8QUUsW*JmdxoWks;1Dr}BU6`thbCP9o?IV z5|(*cUIZDoJu{#}&qTM_b@KEXD!O%m=(Y)qUu8yzLul|8cc3jdE3V$|cQphowqU(AAB! zH6aIsK00}nUI&-fqZc1_%RcC!+S%*-5F6F|>Mj+1Fjy#^34LG4M^I2&9N?ku5KES| zqmHdkB!hoV+fl3Ru7FhY;z}>6+7aNPnm@NyMSL_BWy>^_1-0)LAW?4V+CNq=2=~d) zj`Ve`*7)7I38F}`>D92=kX@0Nvlve_8>8!19jB$6OwH404Xagt1X7OtDFq&q=@Tp zQ;DJIQcmm#HLQ*>`-JQb)F9s+k@@rk9wX5|$g08Xi<%I{uO{bFuUP6X17glxaXv)>V+lON?78TFJuSHp;N-Kih#`e|cb zXzPdWepaUhYS?)cOmk0ucI1+%BG)dTb5hnh^R&2bTm)+mU-^~_;vvv0d8AYB9g%lQ2=;eKAk81#T30kp0z|()z z{&`gALuDskCw!MT=Bt4Gzt9jB#HhRf>2*rZ-oga%rtx}ZMH~{TS;lSLC|Ms{Wx*lw z?rCG%q+T&3*6m+*_SOKYWkhY|um+aTU(FN=tP zDWLWj9`wGfYCZ4j>RPkzgm6i~=uCeo=_|J+DxtXMo{@`doj^rgZ5ORPG5D}xVwi%v zE4Or9SE-L+KnifVdH!USXpA<_&gr&h%%y(Oa7AEY(NTp+kB)}G_2R*+?Se+(VL=3V zFws{4BmP8d7uj0rt$q#@bdz3X8sx7pc41PJ&fV05*;xqduM-L6xT=* z!K;Vg%kSkt#2U4x0`P_FO~oh zy=1!OlC%^xoKF+FBV-#o3#A^=w^$_D!+u2xJsH(AmPJp_wC9o?MxcF`YA_<0eqb(g zAi&7C4IAqeYso(-r)_@;5tx5pz?QVV9yt&s4V;+UZf08_8BIty=PL}hC^vEuSb?&= zNzi)0E`QtfZs%h1Z6Pk7w8#{hS>U}{&B9^8(?86?22+4YJ>2IHw|M7PVe8gz0;0)|v|9eb#$unIX3aCqiKcxYsx)v)P3G>8lG^^+J z3oInkvkiDB<>3%IcA%gN@gYjLDn2vf)@$0DIx|`!6lOhLBe@fNpH^jj`E{QSx13QA zitI>EqZ~L;vjFqIZibkoARqyM9s&0%``7+d3DI-Mqa*u1dwl`Yf9TKocKR;PRHMo6 z3`YYc!ZMNyBy@Fid2LYMlWgp#oMn04c_NgjW!VB1Wmel3J;HkK`N#MjbLf?&<;-Z$ zbtEqm<2jg*Ctuc#*c4z-&7&m~q5ey>h;48}8oRL&Z+&VcD+MN2`^}|)2X@~Mr$+9W zOvocdD2oODLj=v}&#^6eJX|`OseJGfp7XAZ6{7#pGa!BA3GjJ{to^z_4!`8&Rci)p_-$Gpg-lO-0#Nb@t9S_` zifmU~uBYy0f~VmZpCa~uAChPu_L@d9&12EpMzBHj0Y{_gD^<@=U^C$kOR-&UR$iko za4!QDNk`=H%_W?6rHN&QpqV6T@K<(I^ibeLNai}XNb;4L^e$1I?dAB$SpQtWu(-DB zEByN@L)&dfPy4N~c2*K|Qfyy;>iWp7z;xhr(3biHB4F5hi@pAT%F4D8=`%mHiZ>K- ziz-(u^O*H&yU3|r?dY$4@p^y$+R6F$N^PWim%g;NE8GGKD7v0&QQPCiygkQ}xtmI1 zwq_m6Z1}y9BPm2O>RjD*g`5Sn5RI09zr~Q+NaU96?xRh63cpkVzpCSNRgGb$4VN+7 zz*H^Ee)K_e7w{>64Q`pYF4DhlfOSAENnB-_@qO;+aU?I>e zyGwRi=IL3-@;bOHV{3K92V`un<*pS>owYT${hr3epLWB4q<#27xHSMvcqss?;qt$M zx(X4*Q6I9GOfoiTW->n|9~(i=$xtDbGarsvomU`Yl8aYAN7+6n8ZMt7EE*h8IREFk zwvy>rnVZRZ6U)!((_=_2@A`i^s(7Rz@!jO#gY$@9A!xpJ2P+an7>kB&B9g>s!~4vv zFo)*J+m`8ntV88VECNXc=JTF{E_uckd%usk9{$nQCYvc-G0zv$v~ni#4C3D-O%maW z!T-~reI*b+<8JoTrz7t6PKYO~fcHnJe@J>KF`~5RaF-|3E=U?2iB+iQ6?)-D(9UKN zDo1mGz=HhDxogbL(4LZnQP{2Aoko!}dH=>b_FQIvqWrm$^@ssA6H6R;A|Cs*HeIEV z9c_or8n@#8Z@WB+V3bJ4N*ZG1Mo)WHixTr7PUk2y`L^Xv0f?0l@~z7{1{)` zUoq#6OyJ}XCO!m$v-5He9t*tVb_9m7pDdaQgv{$V-0$NQzE)xH7p*xYK6f)bc^s^+ zUwnRl(@CgdvDdvvO+D@)8RRhYFH%PCQ2?FMY|qIgsOBohZ+E6zm3qV3==HzE&PVw7 z{a||hEBkyEZMwIq*SW}8_Xs~-FYx!bwB6BpWsbY8$ef;+WwLFCCDAhm>NgZNM}tgX zoIO{TO^@Z~r8f&&o>f7d;UgAgiK+O!bhaLUE)3e#e-P;`d#elT7LCLy=KJg{465^N zSBmB|fY;+@1ORS`qaC1|=TXV}go1@4R~zzr1;i~J19bcC-}29uyxN9s6QIBretupj zU=XY0_|Wys^YT+kwijgNPGuG^kN4t7s$82TV5Z+#`UI8#)#~y4hb615U^Hp2du&{P ze0w=7aGc!-@)l5`oM|H*P5Ysqhya zc1Vuo1MezFLo&G?t;N|QtDTd+KG>0aG#)q1GH!J`)wK(c4ii}!BwAsbi*vM z+PwuM9D!MXC^x8cGJWR$eVw(QRc_)Bog&a?rd|*hiO5Q#T3o3YJV_tAnV}?%I3??m ze9y$tku*OTwLl)1=;K&F?hrhQ*)B8PzgUxvdwo5t53e6=Zh9#RZETkhoqn_YzF{*e z)cX^g&Dm#$?ZI1RDnhi?RBTp%oW5A6gr3Aj5|G(iNW76X&~1}{9-KU>YX5 z?xruDlMlfC(_1KS$Tg>bp4mga(sFUqo(=CHq@!>2c#128x<|~hAaNw|W%7moQAAsp z=1iv9UalsAg14`s{DJ;|od-jOxp{GGt^a!VWnNpr6{^DB;=zdWsDU0>v~{eIul}D6 zez3{GX_`b>W38!CC}>=q5k|4C(GxNh&cE_jCghLfznzGJ)I^7WVAIq+ZW;)8v75AZ zuaBh|5K%go!7f>~R_(@h+dA1v+9mIsT!BdUaKQ|{oM-eY5V)avU;;K&m630;{}owU z>#LY4tM&Zc__=&d%GFZ;4L%kG`F;7(Rz?T>B?kdZD0~ghwdp&{Asx->p#1Wb@D~v? ziSjl~n>#xcO^Ppn$UE4c@0&;NNlrRrZsWmhJ&8`iJ{miYH>)shBaijVkAQrk8OUbd z+?I!nW%!U)7Lp{FoJ)93^ns=$=S6Z_rU7Z7gbGeohsvIfdPKJt$U+#VS478kLbT^E zVFFha-)hsss%y}9>?V;TO^~->_Q(2Up%+1BB4?ml*vO23b8O|2e1Hzx?^ZLsk?P8@ zV6Gip5HQ;#t8OGocHfB@l`*(bmZglqqob=)q?4ofMI`bW_O_f+aqv zGmJ`pH{9}nZho5trprG)@*{zL)9UPP!z)^Z|C+sN9f{BWl6!B-Z>T9~Q8Q|$$FVu# zSb@y&F;ApnFg{3C*SAuLjZp29*QZxorXYQ3oE#gO=)2T6#^H(r>c<7OdCcdP9I?Q~ zk%6l4NQT8~5AhESMKy#6txvE@Jj6i34&*a$$uwDi(ZXRp#Td4X|IYXDHl&bfdMf^L z_1a#)O+>QL`T_9j(>K%L>hG=HEsS0|2}wOHL$A1oNJ`)diJqxb;EW)(0^ng!`pZ0_@xm z5|_v#ES5YzOr^&Rl%yvfE>m2x#9;fcL0uw$*ir9j^fy5P;U6|o_B{&b_u^Tx=2_70>mMP5$@Z(b1-1k2;>B=1P&9|dblOBVuPvY-Bc zv|CxM&DeDtQ~mM<%wnofz|6|y=0xe921|G#V0nP9GFHMJqlmth&r3*1%t(tCM2S{GE> z-fBDlUy9T(Vs;P}6K2a#Y2=BGtrh5?7aVeIX`z5SEsg|(1d2qiWv0xp^9tCK9$d57 zz1?WzE#3t!Kq_Q&0&DTNwEM>d3vU?{TPG9^Slq*gY(y=Bt%g6BIfnIo^OYli*USFm zj#t%R%g(zi_v9GMZACUAm+9-Dzpf;_8l|xr>uJ$T7~Eyh3(%xTFJfSGwzFhm@Cma= zv*W9M+U-6%{T1akY-#ligOg;*S=OLeAW?vS&id_+(7>%sn@xxCU4&*dNwo^<`q{)9 z0c|=`eVyJYK_cK3Z5{&T`xGdD5tR`D@LT03-~W|e?%y{X^L?vLwpNZXd|><1o&M3& zW|eL}7*xrf^HQYzXa})Q%5D+EmLYsvO~u#^;a&{0Ptvf5>+>_e)zkcC`{Bs`v9Y>H zyY)HfjU0~_rSUhh;;bg|h4-Ibzvi8{ZnX9e+fMfq#B!?9{H zJru7fwPu_f}+&UaT{a}+CJ4;mM)TVxvfugbj{aN8_GD&vsXKk|& z@8>v+cBrv=-C2_vL`2Dk)y)HA9n^&?zts}T77LnAW{uGp_t??3MKGuxpk8H4k5dZD zCLJhb;!Ef9W1ng|G*Y*J5Yj2ASEx(~3biIS{&G<&!9l3=xES6@8s?f$lKWD#%Q=lI zFRkk0R7C`^99no3d-oMa6lT&`PT-QDmxUSKzxx$fn5bY}XL!&pX-sJBs`1`hw% z-c>^I?LZ3cdtaGRri+$2UOeK_$a8iAyR<0D-1fx)PrX{5@i-xWewDx&qA1Km zSR}vClj7e}>AF;FHg@-j=BF}NspbKlk#dLXE(KmD18tNk;>xt- zN`*XM$5k0`=G)(YtBU<8A$S z-L5d9-8Jk;blxlvK&P0M6E;icVu2zMsEDeC@H<4^`zMaq`99n)u8SX9dFYY6)WAJo zei+Y6%}H>Zr!|Zr<7DW4E1Va{>|XG~yv;KEvqcx-C8TVBT5nVF#q>K$H~jr}JB#;` z&_0;IcdU9)pBppOfB3kkMCmct_6$qYR=!d-!APS^j5cb_4SYMg=FG7(mLXju^UzZF zf4Dm=5Zed8(B{yN*I=b7IH3~2ITa{e*)pjhJM3|=m*}`O<5}T0bp-uTvFZAfM#32p&ODB*8^KG|9o){n{CtbLpwjuI;MgjG&T zQ4Me@j_%>07lU3@@ag6+%}s(}5uBQT8I99n$>#gst&f+$h;4QLdZ!;ftF>lESb@r7 zCl3fsN7QhkrI+rYqrRGSm~6xT4T0ML3PAh9s8>woC;c-*+A)H!9ksxU7G5#NK0ed~ zoWQJq!hy9d$`FHkKCV#+3RluCPZ^7!ev&ATp~#RD4dV44B2agcEtfa)HesNeLUE}S zi&SE~f0y&d?1+JnyHV1Tp6z`GremB+c7anw#z4&piBU2@!FA&wyYtLO3I1JAabZ2l zIAsyTG}z*Io{%dcpS4=sVpwM4*WsU*7|$zz@9;D?y6yZ{(ak|he%7A>4!q`@f@hOm zBLebi0+mN;{ab${AJOz^dFM9O=`i zRRdy49ejCQhKd*&#z9;Uzp9F-5YIUQn3a45gDFXd}Fhn_8T3OOoH2)*I#$|+L=ME zs}y_hvI}SI@DY^MCJurnnlUs;8h{0V6;2m59=2=}1Cmc!ui*=tE6Y&3Bzd8toNad^ zxaGNU4-pNg{R9Zl6xz3LDv^`}O8f>Ci)Iw;o7#sxoPMbdW9=ge9Ud^wBjN!TtY0JdM@F#yQS`h(PRB=swM3_`8A4%FiDUBCigzL0jf4 zwfSoU%k#F(%Qifr^DY5KvvH?%VAN$O4dsoah#YtkBK*~=GDvK7;0_4JjjB=;@Ic#o z-*YBh=RZy_;hIVox>)9K&MMQ0HCLSaLsY6l z`QUaP!@XF9xOxvU+5QsJBkTRQt9C1&;&?){+I+v46Iab`kwfq0{Kue2cUNEOtNu@y z{3SqGqh855#eEEt5h|G3^B>tLX)YP<`O70gp~U!rh2WsPhxOYanro<4E8=E!G9Fj`WIwMW7x9!$UF0r@#i`qK$&aiv4D zg;TUeAG#sC&QC8c_=Ve#0jy$2RQZJMjoS(HH#L$#A@7mWm@XdIk|-y>v)G$c{E}%0 z#~*~kcvw8HiHXsFjRgmQTtNThPY@C!30_@CYCny&9-}Bh=*cMbGX%0W@E!nvblU!7 zM0@|0E)6nrVa9GwMDO4UZTf>&wd4zsH1jo+P|?NH+d^EDtuO_HyN|bWqrpa?ESTf8 zc)&nYS<0bT_R#TMM+YCL(cIh&N$AsdVa!VLmqP#TG+!uxCw0|bi4k)*xtx(!M?0r1 zaYs;~z}HkeY(M^rK9kG-F=Gyy8>Y>QvZM_6{ZmVm9Hruip#5a2o515e5f?ZlH|>QT z+l6h%nQrXQUZ#@uj}i>GS=v2!3ffzeNzZpD-x;}s9%z>fgKP_gEB1Q~_5@-r%l_w4K1fJhneD07~`Ng43U2PfuLZ;pGQ{=rJt?kq?A z8_-W+)+`fi9M@G^>QRG%&bt_PcMTsyY@*E9`CI0Rb-F1I*=!@OtKC9IUOJw3d1E)8 z*EXu(qgl&xf@rpejw8c^*lbL&>9ay^ZbLN;XL6$q?*A_ULO{L0x05;nvkJFMMFEdB z1prz=rN6h@dI8D`x2c5zkTnDVd_aS@IJ*JA3b*{l0lqbVbev^S98U12LvRUhK|^qd z;IKfj;1D3VI|OI3MS~|eEG|p1kOX(PMK;RE3J7FXYHpc#evot8yiDE_X{z45Khamqpi*|yx{G) z$z9$gKv@)j!%p>NJN-!M`IgCJ%Pja-p(s9v4d1kJZ7xGAF_gA;Y=cRDuf+)GC_s;Q zO7x`c#bN3&SG1hF%283GBuzT6vDtSw_ir8>6s)qab?X=b4u$#yTS*zHcWli%`yh4u zLrgz2z&@GOeEgh))-!Z^8-=#E7tn1KF5IsEY^F4SlYpe0FS%*l1--7cb{evg9Ba27 zthz%99+*jGECt>KZq0Tg@2bl&=)0_w**o97O*IbO_*AN}dieYVuWd3xxr#o%uf2{! z#**;R_ybS&*wXaw!r%$ZslQ@mgu6`N487jQbSj{r?8oPZ9cG|ohwFw)&$Gt3~ z$<;{|4o7-Gpqwu02a0j}8p|(y+mUmR71FtXn<~Ti92)f4c?4v;{N?;(T6lgcy;PgI z!TkuvABi-1tdB~)2VN&Ap{f&#alo~GdcBFdflWd7;+l8O^WV^Q!C{1nC2#^%wol45 z`j9>j6)w8IVj&i?XVrF_T=GAVNw1aYbl8B+%EP-;{;X4!UHY{S^IU<|WnrXEttIMz zY7U1JZ7MnBtM`^i>QeR~62rgajc=En!R*gh#K0dNj@Z5bw-%!t$ji`tZHHK0>oAL`>)h^LEI*2Y4=NwVr4>|*Kc2SU7_jt7%wfqMnAdN4lKh~ zjxnSlw~k9!zq1mi_rCn^%SzVN!l)VTnSf1++#PS~gtLBiU(xwi7)YXa5%|D=*c~1? zB*e~gmqtIM18W3jQ0B@wh&$u@b6xPUJ2Sk8w)_o4TyC7fhK{3I0*bX2CSV&e^)C-# zR?V!DeiRH~F;idODA||3>N|&Jm}OBpJe}K^N_4QwB_yXm>7l49M8PYQ+P;5{s!dv) z{|oVRL~83)KRqS&ZXboH(%XuEhlPfKjQ&@qtzO@j+z`oltG??w7kpZQ4YYu_bXtxc zjY-?4Gf}#CcPubmOx`XiXIuZXQZo(sfQ)Xp_J);TQR`|dft9|lbLDPhEN#tXrcYLF z;u0~J?dhzZRha0fZun=)%^=mBtp?uz&$9l>w&DrGQtk$`@e(V5(^PwgB!@uKGq$I2q&6VDiE#yYW26h47DG(a;}GQf@F6bEGS4WS2J zBu4|umDmj!#qF!?5Kms9l(z&kos0o+0eQsPhOfAPb61NG4ekmpziXfH zWWZSYfs}}E@2Dusk$fcCh4YFs0~gOF#4w#M;9?JQ)_`EzVQfOpJQfcXytXXA$o#J>+##6mp`kvS~U;L~b z&$q$93=UeERfsj}RiqCh4t@a7Laq9dP+E`aa|O%g*2MEvtH?EH3HP~j2-gqe4?9Q@ zap!DM1FU>i`~n}VyyK_B$#>64T7MOH18!Uiu7*aJMIt)T&3bK_H#(mT@(gn7v0R~} zaGOxJ3!>G3{?b27hW-FszDyv?eaI|EQoticR&I1Ng}se#rlcQ9`p-X)i%$M3#*qta zb4@d`fK66JhlaxvqOEIp+Bp<5(2Hj&VcNiN_)R zXH-$QXeCP&9+h5!r@>$O2N5+wwzW^bV!eCT|A;Cg7^M))VoJW88nKWyxIJAhcEjKC zMUF(Bj;HK6PpBU&LZ8nTjMw$4eIr}mxj|@;$)9$8XQR#Cma>9;s?DEvsCh}b{6DpQ z#Y@6}Q9r6CN^;k&OrRN6U(*R&scI<|wJx4?3%Oqu?jbgRlQ);PTxXw1o0B)Tc^##` zP@`S`6^Ak_f*?X;L-LLLTbh>y^k{9)T&UZqWw@~55_&dewq3Z>RPWGXI=`F%oJ`_< z(j{Q+iw7&{b-(!!Q#9-YK?Z%e7{Fvu&M=98&5kXDP-{|nD2EAtFVoqJUcRCjy;`+G zKoYzkfTK6mU+v!_`?#motweoskl%~SV`Z=zOd39|VXlSxPvp|-t*x7}8$3#Sa-)@P z`kacw4P%%EUmS1!_iLGw4x!j03h&7-qNSH^X(zrQn$Ymb6<`6zcIQL_F(_gXU?T&6 z9*68aGQOjd_{UpWi?!w@=C3vU%6bgr0${B`-cg9Z%*7Y5m(h8N zUBz|k=gUbhl*3Gsj+veclUUnC(jj_(EEN>`3d6w|HBYiN@)o`yh>?7V;ajmwvLb?1 zY7ws^hElw-yeYPu%4pQ+gluCfnTsk;#0GPJ0$C8>GgfYO!0d^;3Cang)ydmnt~Kc( zO|g=z?Kej;VM}3S$scmHZ`yH~)rExZF>=P-!@pYMTe|nm($AGS2b(6&ef@iXy$nBA zH2Rq!x~?tfR%d>Eq?)$FZtz7&>CA0D`8^IaoL(&=hNylE2nsRhh%1(S+Uh8p+sI>X z=XnigWBBIQtd6o;5bFV@=((_0HWbSJB~$s^xXE=EJI}>a!bDqd8m38uOCMORaRRaT z{wPz5-efQ5(oZ`~3U>Lt_g7MXBg6S`z*fY-%nUhu{gP?3$isntSHr4#@GWzi!bBQP z-+ML1{{aqVDfQZyrV-Spf>^=-MHG_ByWKqHoH64a;PuC_&F|0H@_k?8K%}j2Ibp@8 zxjVM5*$&IyC)5GAud&e^WmcY^t?cc?vm}fRUD~_Tm2_a~+*Ju@ihiAcsy`ff!LgIW zyS?Rs+E$fb25wy~?`m$W=n4|0nr^(mA5!yTj!m5p&Sk#~8S7e}NgWA~7bj%Z=q-GJ z9-oE$JAU@&6j4INAL66+=VR%p#4DO=`19o=L9Y%cMKpO{uH5g`H;s?g59PKf!l|Fs zZWZuwFz~{Ar9PvDCVshp&fN2L(%)ZcLvYPsfW^h4o2hv_F0@yGY@JW*^iK?c>m?w| z*DYJSVCSWFg)o(Pv<#Kwl>^3D)YavGWGA6#vIjH|RHwjVWzRN1sRi`PW|cIknE+zh zSNGq|cr3HODT`gRX$G0x9@~8}^UaM;a`rxGq11yk+&Lx!8tsaIgopQ|r#UIlE#mKz zBh#`kO_m5O*6G@gHoa-MBnCmJY*h}@7$}@BX#=2_iJNRuLW_xbBM)?VWBZA1N_MNd zIJ?2pp$)vpqzDRp?$H_kEB&ufP)tafQ8p6xf125JuU2X3R74bY)ZSM zS?qYSZPxofecjW45LfgKDJFejUh9vk+0Wj&H*_t-Yqbg%PJ5*ObkyKpfonWFHCf-W zrpflmv(MfKuTzDM1{d$G@R<`m`~6Ix0Rdhk;EQ}vaLmTYNVZnh6U#&6=30;Y#h;qf zK9a=2AqI1UqLKHL!RXmlV6}lltlaqx4KKJa*Q*1k(kKCcD*BOrd6}kLRiFnzEIKWoZo>>d4(T~K;yYB_yLsSYs8<`5)jtEmBnZ~@TouNAlVj^ z&vSu8PYUgOK3goUPI}FoRJ`jY0jG-P8v@T4F;%XE=-vFvUQj^9Na?{l>!4ij1YUNG z@rzVKnpvTL?9P0#0P*(wlK}FOJRL$_$0>3eLhbN3*Xrs(=zpz#Ix@mB35NZvw8{0S zda+$j)d=ysRo|iSZvC`zR>!*Zca8C52t7uaH2Tf8#Z%A6Y!8#V2b$m5JeKZ23{0V| z0k6Z=T5mSd}9^hcS0rrGId>spM~VjVusJaJ~`Md3XE zmL7hww702H# z*i;;U8Q2EaVOt*Kl0S+(iOvayBafE?GdD-KZkKXKiY8k5jbh^~*nx+u$v+5IFuz@| zBkBAYyR?Fp~t%n>-Y=)u)^gYDmgCr^fSCNHF)4<7!LOe{Tt^2vqXT<6W^5~-*U$e5>Mc^Bj6C&_`OQvpv7-mxAkmu_VH$C&!+c970b=UJ_ zyQ1ZXHlO2^EN5alMqU9VYA9|$p$e{(e#Ei6d*nEYHig_!x{QjPhN{K(z5DhAOWg8d zk?YBA!zVMmGO|CZ4Z&FXud^hLn`R~Ef>Y7URv56G=UskqhqFRCZ}DTd<6o555C3w1 zXM(vb*|E2$55;=W%}QuLeqYf(O&yB0L?`fg&H^aLNMVE_xq|qf8c;{9#>ry-3Z%R8 z;yEuin4g7Eab=z-NV=>?SYh^@%*Fk|0k^Ju$sq2jQTg){;|C+mHfrz_-}E=H`-b>& z_ZdHQxc5&-a>`mP(Om~ZO4ZMA+@cHP~%(?lo@I!-dBniV}Wt)xVyP#KZwbRXT% z#9iXH%eCqJPf_ap9i5zJ_ajD?6~Z4R)KNMOnHD~T9`Ml|#E>lu8NLs01x2U6B3t-b zHY9}_jB|G6Y%X~1`EDN6qI8lGKzqqosq|O#R`AuF|uDyJ}4K3WGV6~VRFa5yo zxDOKq=D63=?&b!yHXb(_A-4s8mldWlQ@9j7Dl&%lh?n9ysmU4L%zh%dI0S4d&N>Dc zeD9F!qUePo{DG(I(d1=u1{SdtyeW8)B432SOtTIdCVu3ub)t0nt62FKaeE&g2Pr+* z+%R#~GXdX|C6`sLY+>QCY8~E08ZoMq1ACptMe4Q$4Llk^unEpBvQfEzzkq^mrmR0d zllA)|_C}iDnU|_zy?QCL6=-O`foy|0j(5w0OS;CcFFX33$3|2C46){~zmC;A)5(T% zr@7K_p`FD@KO?|%pY3bweZT<2X~Bg(QYde1e@n-Yy*&-pM@dTW79S9~qXI8F`Byrg zr%xh&OE0}PgUzHSY4@~$NT?4>eZZM$*-eWCI`Q9%vj1p z30CFoWb4%BN!+=^@?_NMSmS)#>o!bab^g_S=U z+ziyGm3;dPKvB7W8{T|f>J4G;pY=T7c=xnCOhzTmm`rgk5GDPJ00hY&qsBIHL}g%` zzg64uI1$6YIpOnA_~I=}Gj@~BqX;5~`#~}^J)_bUY0cd5qT$9{1%BNr0BPp>VP$VQ z^*Zk`^QF@A4Skm)Kj?X>cF#l)EqieKD!%U$0eM2Sx6y`wPycXBImzwMsdCKk1W)@1 zwjH_4U!QIS8u!Mr6Z%9pNkzDXDBGTRI_UPk_ywT_N&jf{9du!D`V02{e*T-I!Qqq0 z$BnPVZS zrmf4`JuYoYs6ThH)hS<6Ks)5?{pGkEK_>LTUiv=Ls_wFXHNu0WSETFAyYA_Jnm4#; zO6uM}(tW|+Nwusz;73FNqJ_2nZn`Q0^CYCZQ~IWV8&&(x6ss$v!~}l1wFOT;fE#o$ zhNEnp=IO=BTABA)SH`5*yd33l>o=$DYW}wQ=yA4TTZbhFO4Q|x_1dG3)#m!J3(uQY zoF8Gpa?~p-$I#5i71{h&v$5HKn~r4n&l$UJzoE)N-kYTZHcn9N+vjDZO4T8$ zxT2MR^D_x|gUG;Z@kbFj_&-nJJ*@8g=LqezmBmjB7vgTr1|8-X+>owz-UNY=F$Bc{ zBiyeuBerUL3nXEpoO=pr)I+fqxvcKEIj*+&{LXBgZTdO)abMTj)NhM_b<37g- zzS*DYx9UdCs$$IjYuI^VEf&E*1K?`V=b!rN<*Zw^x=!hw^#C1|I{np_cV6{PIWKEK zT4f#d3uo8+`XM5Ji$v_x* zEv`AJYkXZzn)~8;LONxt(bT)^!_cFb__)XWz$QgZRue1zLoVmI>t}? z?GuM@R{e}pcWt?N8H?^7P-&Pd19P~7#Z>jmAeDnD9)+Z>G~+1nLVRMLtOsOkhxt>E z{X;+jpe$V2u?wv{N~~dj-}}b^FZ<@Y~Jvg{E^GL(QKr%`>>#f;R`8M|i_r z?h-gd@j$mHQ5LI23*PWe9Y2W33lf`*@Tb$lyomrQkQybPseLOzu}}k!3`m!O3?*Uu zOuERbDOir~TykT7mZPO&Y=8Mks%LwJSw=v-=56o`?0G~A2FzfJe!Z75)E*2@4F#TB zLpv;If7eSFz1nTJAI*>(098g4jd=^GOBlA}9BN1y8u7fRpKl+@nE|K!+X{9v$0rH9JU24J|3 z#4h^fJyo10Hk||TXXpK)t(o_VZc4~fABfm5cNXQEjN63wK)^Wm?G3@u@2jf>_q}l< zAkk)450i#}B7jMx#h;rgi?;{+#^Nc0K-{c3y%-2<`SP8H4=U$Ts+Q`PQV|F#PG+0X zB(L8QF7W@TT^P|zRc6$)*kWFlX@f~XU*sv#K;#z_uk3c+_iHK!pnwix%Gf9JNMTtFzV0w_ZydG$?{uf+a+1O5F+`&DBhiY6y82 z(0;~$6FDO1AfurxGmB*i7I_&n#X)60|peuhX;})F^4bm3Y+?M3UJe5orTICirIu`V3m}QAGgF)Uc%spp3r)2_D zZ#mI&oBuG{V%jd99q*OiC93Ej0TaG6n>+Z}k6vqp37o+|n-_GE3?d34Fx69|Wbqx$233mKiR7 zvtRIa{<|c{fNUw1iI$1zgYWb-LhAF9_u|gcHHK~Hbne+c1CtfsP{{9&3L)tY(9m#2 z=vW6PDUH}Vy+Jfzg8Wb~6caXJBX}(er-NqNIK$dYNZ~954`4P}=7%2Ct~U%98!WKQ z9Vyo;f4+74*PZ0Z^yIa9DUla{nhX#Zcc)6I_Ai+s{3bLjEgyy0LNTD7meeet=`6<$PtthjCqm;aAH zVTC;!ZZ>BUrmi9XSQaVKk5B#Fhxa?JL3?^@(EnNTH!@1&rn9^-d+MSp2%22&AY(qv zfQU7Ro3=X4fCLWC3FzE!d-FMW{S{D_=iRe7n00E-4MNpY&nva5e-xMLMKvpm*c^MR zu>PgZh+NEd4qmGKgc?JCG)p|7uaR6b=U5c5(ljMz{WiUbk|U1#fLw(;=x*NLa%y1I za{s*KO6sKJ+BLSMAtWTg5833F^{!ECNKC`TWUiIZHTeDN;pf9%AY7^_zEpMn3#n;- zP$NH=VLOvCrfcr_$CQ-F`Mb+}cD%tbcetc)BqDIPjhv#Yljk{qV(84%F-Hj4Xi=$C zzCy2(JWFR}Y*cr-l3>okq8HI$f$N%6IX#4f%W1He;+GihHHXrU9t^jC8))K%8)R|m zU8G;zIncl+sd}iV#{-Vmo!K*IJej-&uIsP^yB@YPWP{{7&aww_V%_Cgzd};lyHSWv ziFYI4{Jj`WehlXdCHuvu?JthZ-KJ=DHTmeQ*Jjjer_vzEZHMJ)5orI=4ym19RC3C$5Y2n9& zfswA;;>p*4+h>w)DtRYq2LH(V4P{4yaIL_Yo)zW5>w|K~7K2?9JcuL?r~3fJXTPA@ zdr*m?Xa75qe^aAZ z+#xiOB(<+W9w)e^p&RW?<*ygcltY^Likx#x9mEEI#1L7gU9uNisbhIk^8ad@74DY= zdRhHG8=zV1{Tb+sQjr=bJeSqx73xX{RBM%ip)GNM0wTokW^FE$W{lr(Y-tnd?`oMe zVmBhQbqjFh@u>X~hQaikGZFNzhKiCK$<)kfO1tWUV?=Lj(BwXJ@#u7NZER~!WRm!q z-}mBwhTYh&rGx|-29JCk1+S%iRbCt6=COj%!Xp9UZ*hZ%07{T_HN8yV2ytNdvbwSoD+eS3Gc(I-g{j_1}t< zi<2cf?H8hdwB{-6ZgLXwhl;U>{16%WD9v&F7oKR#pv$OW=h?cPTpg(86q+)~or-$o ziTn5s@Dr4Ht)v3KmKyCpb~zC%v;+WkOuu7J{_4f?Ky;~gfww8sdkzFWtmt3ji)vO%L)*7WV*-DPbU zz_~z?s<{3ItJBPqHLK)6eDK8?jAa*Y&c~aZbblz{(j?1U#GEG@B<@8cZl^NO%#9mG z#%yj)=_%oX*Umbo&<3Bc(f;RJy_U%@ zF$JP+fBo6C)9I>C=X#pW&y^-oTC%3Z(w?a3H*kwtyuYvL2U^pn>}x)DR8o4WQZ!_e zxi0ynrT)?JGQg^NOj-{z<7CU1Kj);0ATo_9E!`cu5Fg!D*@+U=q&L>V4}PS7hNnfk zj|H5yOwOK-%X6v5SFj85Ixa~B=Z;}Yu=If53Vqo(p(5X&-4s8rX{iaUTTIV-Rq)ZI zn#MrxekY7?_C~@Jvul*JGoR(XXj}E_(^bBd~n~<$L6Lx?7g5LUDFJ8i*ms=?Y5yCWm4yEioSU4bJT)A{PYimy%KVeZiEcy{@ zFvsx2#zWB(h*(OlFgL`$#!ELQb*s2bUU?r>GVSXgUkB3WtShF^{8!5){@NAD5?K~8(eCXPE5%%VN*qaw)B%)wqOv4$A z(G+{%#gmiOD)2NXNQv&AnHMxF_lyb}H3ALZRXj20d9_YK7F>*Lxrb|ojMC+`i$w|6 z0O4rw-N&x$4o?CO>2f7rG{2EKC;w)=n`_!zX{y`y+vj*8a_uL7UzG*11>6BO2|}Km z;!$8Y{kwnf^z`dQzH)#TXVIG_i4d)*6}TCeDuqwubG*j{Gv*$h{xX>Ixy&n{Rg9APH_!J-^l)pz=4lTy;~-& zK~=Fn$}4b)#dc#~PKo+atkN z!C+o$05d?$zo*qWc=r;GMGi}2{3iEprG8l3tz8jEKlO(iB_4Z){Ycphm2=*)kqdr?*0e&F!LKRGj{7UFH%)rnvSh}z_pnqy-CV#`XEjrcIh zrgBTS-7}TwD!W5LudR;^^dRiiH-7$sNI?*WkLvHfzu%)bB`j%X+nd=dBVeFbb}2fF z{4SO4H&TMvISzg>P_|jZO~b|EzNSKUMs;}=O=`a*e}tlme6%A-RQ>c;^{{tpTY@LC za@wNr%xmgOR(wyU_G@dww~f|mPUq<4Q}ydpZ40^8-_^ToM4T_$6zkT=emgC^H(O+| z;HErvme|c{tIrEC!msL+{-UWT0pB>V?bGz|en7gb5A$a=>urQbImLTS-YS}j5ss`) zdY(#=e+?*~cGs-+1dlR1-sPsAVp#T77uPlYXoyIBYL~dEFfoFi0T$K;mx=v!6*qZ? zdBAuDs&BTbssA=jQ5v0=CVU4a{g@;Ey0%B5#z#cC+R0i_0Oivxr1jooX*is98*+Um z?6a0R)oUN}^tZmHQR-edmX{;CBRWAjwqSH}fA=SYW8I%j9%C($53W%HlV8>_no3$j zJ>^zEF9v4nWdgp?J5JFrfjUZ>QR}42#mENAHI;t~7uMGlO|kbt|GaXXcam|mlpi*3 z5`q&35giq9<7r@}jibq_#xW{8>2rfW80D7wuiQ)$k?E@56j!UjMZ5-I_yph|Nn0ph zfBZQ_!hIvHVF}S0X8sH#2m6if(o++%M@Cv^Pu~StghfwUl@;;3?nb68%j$LgJ{bAAK5s`bP#f1>{t>SPXddD)>-H3sgvIO6{M2a;-iR!S&^)iy_(Q;P)!?9u^6z%=|u9};Iw17 z2tWc#vSOBN%EuRx(#$z=b8LNLD`N;iT<;hCFrps|nFNx79SY)W35lqBjx>2V*0z$( z;>YcAdVG(5uPZA14d`8fX0P@ke@_HAO?l?jRoye*!3<(>w2h zbW+n6#oHc!SJj4H`?^+?>yU)yesnd| z)|C7|Q(?`?pBSho9+TB{uty`lwO^{6XOcmh`J6p8rOpOW{jd_tN3lbDPv*Eb(>L&$gReyvVNV*h=tcr=s~nrMerIZ>kQ$Rsp* zqmp@kacI4&&PXB?z7iv*Iu6f^{p7|Nc`HcnEhau4p1md=0bH_!`&LRFb%GE-UK&yN@p@;o zfMZ*1lXJ~bf4rfng+VZDuTxc*Hp|Uhx1reg#{(7A#ApemZl41^haGxeMIDFSfuECD zmI4L7C$dn6sj-DsJ1yTe6u3=)Ugo;Za%}Jb0AW|Oo9r8M)b?Y!dTl7)N->zxiTQX= zdOJDavkuVvFboc=?fp*ouxTCJalk@PlRPq_L5m=Tf0-smaBD=d8pqJ*GP4Q#ZA?v+MuP+c#c@Uzh(QmDevs%=1fszLVlNDV`AVz9x>^v9 z#limr16(i>KU<%Mn2*Jl55`47nWU)V0|_+}>k+-P z&mL{`4{MmAT}BDY<-1_L2pB3s=dM_^Z4lJ1>o%alizYovJ%7rp4h}*<%y5wZ?L~D; zsrT->-D}caqm9djvtM=7pY9jptTvd6mmvK|e{$3%g3kUepXP9orodt8KTH6f_jD|X zp!R=5kN%Tqd?zF#L1O{e7f1`or3*1$uH$HB0hq0w@@4xz-76fv8i2=L?)0%ANWP*G ztUc6g7X-EWw-u_@{D0_425C%=nx$a4HgWzORt5f%rpSt5{6%Az3yzPsCN zl2S1D@d|ZW3Fj~TaO!k!UO&;*35@lbUUF>Taf>f~E-6$2krD!maR zxLI>C?#rF|WR(Oi?xb;CBlG0OKZ^N5M2)T{oHBEPj?sUB9uL=`@kS1f6e?--r z4~DbKhZnJMXyKl z87CAXX`XG$9X%yute}+zjIo^-pCU%$U25p8DNckT{m?Ni8Zh9x|=zSDd z`-69P&5uL^-Xoa=WpUA53}?T3KCJ_KZ?TLF0o2>%UY{SDhKWe?Qx6_b6ya zSDq>xc+U6qUk3UA`R|He`>Wf451IOCq+SqYq68UtqjkN(R_gDx)N|)xJtO701BTO;r`sw2>q3@IT zS{JTu7`!xZWo~XLSe=B=e^OBF>YNId$rA7}r1*^WThw2-M59ENp)t~0K+^Tax0zgg zB&M(l)^G6v6fENN{3z=KHpIKlp22jKpij`~(=-b*5AL6M5>gSv>Ak~yKh zYev6pt<)Rqjih zklviHY2~QCC0wHndz@bL9+fi8%3rOLQkDXb)&=4H%CXm+6x|0B!lLP-?8u*k+7&+v ziUu-1JdCbJ^706(1;#{ICSgVhhEih6$=->L_bf5)7|C^PT1gXXXJweh-J zt4MNv_m2i4jKgrP@grt!Q-eOBq_j*&v7*?8rR~$mW~;YJO#ij>c7UoPPa>Td@fbvm zy3u9P2*BWeeq=Zm6>#;7HDYsbU)bjtB+wJ6&_m(ZwD+OwSOS74eR~EU)gTw)@lg>K zsglGI!P2Qxf87ne9QZ|6*<@d^T>Xb%U3y^KV;rx|eLc*vks&T{GhUxokSG6QCHVe& z9r1MH#!P5pC=joKEhrY;3Y@L1)*d)Tq5*|SKz~r{_i8JmmuWM)k#P~vCJFrdFi=OA zKxjB76nB47^v+#8L@4}Lz%7~)rtz7Pp=-2S>U{vXe|f&ehd~QzpaW(5gTv4aS0wsc zVbs`sFc8Z8@uol>nv-VQr~i;tKly~sGlr-|&c4e`%8I*;PgE!HFTXe=40KOj4l<|M z+Q>FsC4|qCtVjVMzyH#>52?1Yk4nO=g4BpdJ0z#okvrM7l|szG;_@6K$jySZjkFXO z;`y{`e|1dZGS1oDWLk^G_1?;!aR2VzYZKdAZy5~1MQ=e11{^vtj+M#uR3iy6Se3BT6o9#ScP7& zF-d!vo7WDdp2YW|j9o0wepGrN9>%ziJx>$be-E8RN%OLma5Gu;@jUJh=pgAfkRfPPAb6l7|wO0 z!exi-3=3PYhT0$1#FbmGJt~%^urNn|VO!cX``?yL0U8NnOTHwfos%v`J7iC$)yGtS ze?IQr==YEKP~^a4qRbh~Y2KcP8nsgbu)cqzmb~Nd6ZmTZ36d-$;XNZ@e%J7DgaS4r=_J3c|$)Sm)cwMetc8 zz`@}RinFhg8#f%(pVLJe2d(ZI^dVV_^1u5%5^|W`9BX3VM{AD)&U zL(5Rta|K85^s25jTclpL+O{6!Q(NT+w+cAVw`*my>A;e1Y17WO2B+SApI%;SK^Cvi5+~3} z(p@2>-*kvnpfJU?`5%AIG#;n>4Ur|e`_A62b+pNOC9*gPBA zw)~L3L08mX58pnl=i=o`LtW9~D%!Yxh&Ts~Su=YlHQ9Y(DLE@``BAR8^J0{)Uuavh zA$etF!SzCqRC1reSGDhJrt=#n%Qah1A$2^bZ=Wg=f20P4)J4DaC{Z0};x+8)(Z{uT z&a^A2kr-170x}!1%_64Me_yA^D^k1#0x9vhKf@fXe%8NVi4c}e#m+TVP<7ivMCfx> zj|1PD>Pqoog~ZtvD+?qQ6@wHrg{!jhP)01Gbp7|Pvl!(U=M>%me>OaADZ=+{#FgU$ z1L*WaT12z5L7$3S7GA}@DG{^fTtBE*5mZFt$84~?2M%fV37xo>3YNxg@mppa-+!I+ zmz0~WX-G5@%y+y*8I3)b1NHf6+$Wt4<~EuXwk)))P3aQNUlA>8kYzJAPkCQ+L1T_h zVoM!W;IG)MpD|E}fBw7Pc@gl2@}Yy>(ylB5^HgjBIC^F2V*2<=fwwVHC9ry)l|*W8 zP0Dbfj(*j{vriD$E<%bHwCRReFA~2aiBuJ^t%2k``vg6K<}Uh3?A);BhU+iMRGGMj zm$>GBt}GQQtY+2iM!>wWV^naJ>s9$q0@qUnyNl!d*)}$Ve|%Qn`MYXI+Rsk2Hr!a| z>du+Dc%BEE;u`Lt&wBSwx<2ZJ2ZPO%~}n&-$sjEqnVby%k=b^q?`F0r#-fx{WWC|U1wD6J>PCsdLM?7kPv*Bv+6)uC{Qff0f7Yc+4LJ5ujv!cl*rC>eQU;qw`l+FMeCu0zjAqJgWj>ab9{3iO z4)4vnFFm9d)vUdmSW37gW%P&C1V9%?GY|9~Vi^Buf6U@#^HaeiOYgFYpTb)3@aBQW zqN|HcaTT`4E(TXaod9=HXQQqUPej{fiJ=)g3m&LN8`%tQPYX1hIZi4$iwPciSDB;c z$?t8J8QC8Wl3g%-wQ6}nLs{gG)O<#BD0qfm?A1I4|H`g9w|IplpaIyBFriue(5osT zeMwKrf59WAD5FRE^$pioD#L6QM=7#t*U%aqAf*S$Zes`hjsok29M-^A71or58kyos zpY=vG&~6y0Ch%E@-`U2p?@i_A&?^m5&ghse?F}{uZ3q_RB5}}a9!)zO#}Z&t;wf*E z<@W)F?gk`g@<-uyfx4|p(fgXX9kJ#xRiSJ}e-rE5ST9{MHb#7-H(ESUViRf=q^zV- z=a9H6u&C_xHOXjC!^eaV8{*{I0ryIaAj#v+nT|;)lMDT21|yva3(uI(t!|=8O>pzG zjlo>qd)!jR6d@xJ{UyE6H#cPmN&Tx>Z92n{`=0~GgjTRu*n4wFV2?mYul$Hgfpzb} ze>u63$g62TVQ<-HmCfN^L5(p&lyRePClFU<6QZcf)#_I)!dapNvvuE1UqX!6^(U)A zXZ^d(l$0)S+LPY*0_fe&iRFKGi}o3)i!RwDf^Fo?;`*0fd~+gOwo_IT(h+PAU_A#7 zIOYgoon#|MoZ3y3G2qUXb7*p1T6{vIe+dAdgX0oIQt zyEC^@k;$Yteb#`+1Qlf&8riWf=J@?_gTig$RgSf?HAht1%16;3IaUEoekYl-f7xH+ z3#ZqMSkqDyC=btH&9I7+1pdm`%jtrK>>)ZGmv~$ZeCOV`u>{1N#t-Xbc=Hb^oJD38 zq!oP~6t7UA#$zOI<(_)gu%A6S<&mGm26t2!D-H7b3VZ8)7FI8}OuIFpR}ygbpnyHL zJGaj$9DLl|I71@L^2_}at~s<@e>Gb3?)+HAI=I*gsGc=e;s9)h**#H}zFlD+%@;4S ze{B>WB<58vZIb+3YUFrjdC~G4M)y>!x1CaymWbPuMVk)EDA94I(Pi@Yv>!7wV&ud6(mHnfgPXzoFmXz!lKVBHWG5hX&ujRo$X-dvxB3MKBCrGIYz z!3XQz)GcxnGH9gHd(T_M;!$Tn#*%Q}QkJn3p}DW~D*tmeSEXZigM%4w<^0uzL$)05 z5WhYNSOV4I-FeAA9SaWLf59Ija7aW3u$}~0`s@%sC2Uy1tsic?*fJGWPc$HSZ?o<3 zi^{aD=iK3>o$PM+%(*J#GoX;FoJwli0@NIadu-hvm?R7^4~y7SvX64*;-vM?nHSel zbkn1}sZC%!nO(9jauwg+X&>~&5>4kaeam=X(x12M{V9kb7kOa&f6mc0$7SgHf06c; zQBi);yNXJOfYKpSA}!5ON=uA@(kVG~cSxg@NJ@&N#8AU9w9Ehl(m6B?14GA9(p-Q4 zyY5a-PVjvPr13j#4pv)oKZ#I*A!G^Ze%7 z9@g)fx)BFq4@s>LEE|Ck5uI_e}p#qui%@vm9&NaIy6Y?77FHL1W2b8|@5kVEoD$pqL>Gc;&t} zhMD(s&MHIvqN!dWW4X#P((3l@MT6@uyMuT%&t>TQe=`Ds%?hVPXK~7p5wHA%BnN3H zgo7Tg1{#1(N$;-ia2D|Ofd{4ax$e);{?9Ccq2W<)<{@^fru)LRhe?z9_r>yg1q{LO zYPT+3n*>46AExHBYdyf9pPjR{wEtp8u<|%xW%KFRSNVJwy$>p%A^{57nPFb?6unK3 zAjPsif1B|cT(fT5+hy4r$Mbiwqoa-rC*1sZ`uyhii()X+Et4hn28c3-!L4`vdv$QG zH^+`J{m7>;#@w?F2E3jNl>wk1I%dMU=RUMvp44-a4EW++j&)qzbm^{SbLAH7FVXI} zXtzSnSF61a3FhR(FfOc1;xE%{_3!?~8}JVMe|uEZ2(EgB1^9{uS9Gy(WuxFed>9VV zuELP0MX;lUpz&|`NCtlnJp6`WPjaauJ(c}hI(W_f{JaPVm+&^_^D&5x#iNaJk8;b8 z@`3C@mP@QAHeycIQxYJC?v0ytJxu2-+l%yD*;#+N^!3qw_3Dg&Y22JZFB)t_?3g{$9`3_1oE#w-SuOTK$}~e-hr^ zOnzqBu=rBHoR#>$T02+pjUe)F0{Nji5T450-HgZd2nd1BP2$1{xHG*-c2C+md#+)7vu5W{c0z z^bYD)nxVWikoN7+jf~;7c*jNMf0SExc|NN;8kN&|_iQlrYy8_8sBQZq@kxp6ao5?z zQIG7aKLI9x;C_hLWTAO^W-WVVh$su+No~9udR@PsVd_>g!rH9l`qsF6qy||9j($A* z&pmTr5x3dp%F{5!~UyR;#>y2>i0C%|H0~MC0(t zjhvgA!or`gcc>#sGHMDHe_J#EhMfI;12V8sl+&m$U_pgdOKsRHWqG6x3isMPSI>Fp z8Pz8Wc;6*ghdW!zx(b?;Q$eiKQtTYcEYCYlb332q{MX>UG-&`Pm8xH_dJkSIG%R1Y z>l&{w4}i$|TprwNW*cRN^j+Yt8{}S3xUr-w(*aI-Z8kg1k#>v>f5{GVS}}^1*rv`9 z)ohWG#1`9wKAavE@#1K1C4we()Pe8h{<5Ptf7i$UYgzuXFa)=ovx}{L({HByjk2wh z5kjb4Q)3J>aPQsR+{t9JQ(G#nYF2rTgn0l#bJR_pL%iAD zp(E(p2yegZB0zsj3W7RnhnN`ya!dQX1~T}?v|7R|4d8QPPJ4oaOv`g(8_N(-$oY_) z_B20Xb(;S1)tc^)AanZ_w~<12C^@^tMovlYzy%aR`F<X5Zb?NStg-Or#$KZn}8HK zr~gUJ6XfJ<)Yhq`BRD8&9_4#@*A2mz3Kl+L{Kk27Weq!NAHD%ajsVHm2TFp zX?UaZ`r*j}D+PG8cWWCKp~#06MSo`NGbI1hr!7DZn@I8$>hiiKO5-Q+ILCV9q3XsL zc2c|@eOQq`*JsD*3!!+X%7F5dZArWOn`$5shO$vx&3&3I3V_ z6=S(VyBz_GtGuY6WCdfQf^MuaHfZknNun`bw0lLf z+-0E!#rgGZJr`6$Ot=2M`M*T9=lIj##3i;qI+pIO%EP=_)+KRUn9w3>YVk?f*dLre zoNi@}Dr<5fCy~Ysg=pB=nCEHXV|>OajJCgDenMfAv9q(y>O?>|YMEOD6H;=f^LBO* zf0^i(`^31%^HUL^HCOe(%$V=)dgIROSV1&dr8NjWgTtUBDdb-5NhVYa?hbVtmQ;GV zN(SwZmvcP6+?_qPHLSO(^EbT^#^{6}Ui@h(g=Sm#3qg_6BgygL4+8FeA%p=ldb zT3UTmnFe01wblB#aya`YYuKeSU7EXZ_;gwW39^caKwj~%M}{Vv*nLBA7`Tdie`~S) zcquQo!6$rL;&jmBv)*S~JOo;P*V@*Y{7)*g^$(wmwi=G}$o(O)^~hAI^MxS>>q6PU zoZKog&Yjl(w>8^X);htJRAzg9(*0e;cyL&}3q?lulXQ~*qr(FJbl0cqhNWs_Nw&ur ziu6e%ChJKf*Ih|>YqEH&U`Vs^f5V1aCa<(adi)#x9%~7!n1luPhlr(n5)av)L{g$H z#or6vbz3JANgoNhTY4LYT7BK;%U^x@smt_=@72zW+J^Ek_Yw}Stq#ANkuibr9?)8@ zJxcHo5JIN+`wPfrSS9SPv$L*txjU{B3tW1c7JUUX6;NrmzEYQZGUQI6e;ujqVQc6W z+$JJ5#1n`3-N=%Sgr)d)vjN?0g)~4Lu<6o(D*N*6>SPF$Q8uz=)hYUtj$o)u%!J&H zH8@%2)Tt@?RW^ydU~JFes7$mDgTsmd{Aeq?L~1M->j(M$Twr+O#IxAt$Mws#ZAUH?xftch~!iWRZ;D5jt^ZRxW~SJriz{(yL36`B>p%-w(? zc~i6hD9=jAAd|*<+>{|w5wWs4W=-|jn}>|3h+)^YUw?ae#4o0Yf9eUJMctqP3jK!2 zwDsINHbGWogB}*Bm4h$gjS!R3SGbAunaeHJ33)Xw{JBywOzn4;~KB&<7*hg;F*q!wP?xDr|mNPsm1SIGYE zCZPsx;`HT?8SMA&e|xi*V52#|v1t>AgV(YmouNQgSxVjNtcPvVlY-<-l5`zMCrO8E z)WEq?Z*wEJo${UV$k-C*zmOsHF}!$L2;`|Yr{u7usZyYR>?oILav(bAKAJ_(62>($f3e<+mnL%)Me&~4EV9}F zW5%V)y_G7F`~_I)YBrUfh+ei0ttjmpzUjk?;h>d(6>6?>8 zS;$ZG(+;dK z>fXr*??#a>e^SKU2f4hPC3$}yUw`0JbqC?NK7JFM#i;Zd{t^_RZW#UBF^Q;^WAxh( zrx-48tP`b-BK7zzRa_lies~NmT!`8igySr*U*`D{d9_BN5`~H3>c6TMnXo)HG=)7# zmF%PABs1QF5k~5a)`GgP={4abf3_GLc=(7(X6}FGfBOE=gZu&8rX}cwAb95yMWo2r zd#D1;7m5jRRHZOiG|+?Jaqr3aP|pVt19{q5l#q_+$TMmWWhX^aQ=!BD3{2ASNF;63 z+Y*>G`zLT3H6Fms%cJ_W*Cq>&XDru`1I1~|UL}{!t{ILcUHaK2=`gGy4L(a5ZHwYX z25Q(se=Y?f6lDR`XF4igbHy*ga@pK2zqj8$cs`eQI%BY;!p_S+|0|AD zfBbejKj-})ka9)FgGagUSLI{eE&}iSl0B>xM3?{e&}LhkkEuAS8n*f-;+~h?Z2;sc zSVK@vaitN8Y97Yx zL8pzGA0!fGHS6zw-yTAf=Fk4P(ng@Pf0@H7*H(zGY`XK$poVjjze+XDbEOy>>ZUYq zoaY963=(@}1xK(=b5CGAE`@=9KY0GtY;ZL6g|2W;`NttPYY59Zzfee&pndd4 z2ni)iNkMmcWA5vqZUm}!x|KO_J zJW-(4zdCV-aBkOO?w#c*HwjeQ`VONL;3u%e=Nh)5OEgq=nf*;{$(rq2`Z*#W>2o7E z_~=1Z)!Wh42)5_x2j8r8Q->?{f4aVkDpK?R_j zUeT3tx2!XSH!3|~>?U4!?;=F~iubHzQQA>M!75kRH9^A-n9Wc#7%}(Juei$bL`eLp zB)w>Chrm10783RW>cNLaOlyog#Y#<2^gm+>ac@PFACR1|ReLm2^gLi4&lSx!F)7`( zE|yY7J5*jG2;`BCx&=1RezyIBUJeGEpG5$^Ck;RpxU#6W$Jl`Z+sLW~nePXv< zGBb8z8Wv(kqMr0Dy*Kj`%XK0}Cyab|I`{zAEcHADZ<&P?Vv%wzlH*P-endB01NTN^GL`*{M6R#Zjw zChkPh&wTI$*jC+qfo#(B3m9(K%1-m{rTtyHdUyN{;QQn;Vv%Cp45ih|B(_i=pn(|k z-}rkFbILd!vr!PH&wwJSD5Y2&dlPhbL%|u9RaEB>IWx_^e@83n?!}RV*_p1HxDE-9 z*bp7U9(VKHOSbo_Gd)ypIsDR!UWnF+_}mnCFg}46J~ZFxJi$?gQMyf&RUr@(nlHY9 z7z+NfDaEQ-U=n>_~r2ne`P7gL)djQR}l&+El)CHd98?2 z3A{`z(8O{9wwfON(*|sUmXhx6)$H2tqfZXvk~`wPS(@~vxW4dil}Uao*pM<3ZGxtQ z0BfIfUJUW4z>_nJjOoK|=M@+PN!YhOkGK8T9qu(IM?Nj^*6LDparH&=nau3o?z$WD zU0E=Re{6qK*^0>LL^sHX(cGR|gxQFz!%`}mxR*;wK;nxtFN8dgRc6B0U^G#YcFUC? z;uW~$5ASbJn>7ZydS5*Q9u)zH^&f;Q*Uk4!Ab-trGXl;gS6L_3#j^DTt(m!tO26rT zFx{rBX3ur;{MCWMQ*jYSWD+Tc^00bediK{bm{w;|z$sn})wghFpJyqt)N*(GoH(n!bIS!5mucd|6t7&l1nRLga_2%QGj% ze=2~wev+c|D#c_HlKvms)N_ z%)_j#zv%BDQ!}DsD#t_nTLPH#td?T5iF^Zh@$iybp3@may^X{-LxPY>gs|tDKgq=Z zFiZL?;Kk3Qw`yd}{Jzn(uuclUcP`44fASKLxf#?e)Ds9n(g#<%=KIETXuIew@ZyRh!xa^ zgLrL!mwG*&{uj=7!WL4%ZCxj@Sh8|mLe1uOveg7BpFIqyZ46Ymo)HN)I=5+toW4@MtCpdObw+u#VMW3N1TMl4lp`3;#2i-ndR{>jMRM@Sc zY!MsFEPE!B?-#EeFB{969LMzLd@Kqd5h6o<_ZO;7ibL&v%_J*u={3%@f5%fkC_fvR z0n#S0B`EE!R9J0Pj{H*|_wyNbGii4%27cz!^nn%7sm{s+b6^0G@_rFU=cnp00FIN*?PGiO-7KQ@KFzMJG4O*X*wwf=Yo-HX{B zJ!PZ5{-J(1u3;h4MSG8iZ+~$_NBJvlegG?xTtmVJtU{k6o#*bx=$FU|1@-bFpvU;k&TxZ~^e*t|1f}PK9MJ1S| zJVk>)_?DS&Mhw+pkU&EQrn>pgJIfV2c#X{I`;uMnsP~!R&6;l}qf^Yc+cll6OIL_1 zuFZh2a6uZHg>HW%Wak?T&IIHwb2TI6+HJ8zs`PBIcY-_YauU+ANm6(n1!j9?fsaE~ z8M%mecHj~vitmfsfA11b(_@k#Y$FzJOf)$(7G4WAsm@O$v`7YMPNNV=;L|H-^W{^w z{h)%ff-f4~5HW2pQctL8pJEng__YgBg?)5MbW!6>bVsT6S{@ZNRa@5S?Fe^#FlBOX zKmlR_h`E&byAvG%YxDnoo-}}H(q)1J217cUkKOjK{DSYWf1$+38~>Yt!Dd;jm>ncG zd)$fba+Fl!>0B(T;EED)wER}+E`Pcm8O#xFQ9XVFFL}D5Kp zXjKek8bNt@FLzQ8|6^@5Wp5thkf@+V1ukfKk_X_s)@Y>FIm;3{@l%Ta7Ya~d%ao;&Dd7(8u;H*j<^y8N z>U^JzvB3GFKT}55Vui&HOd~l_WeRt&y}MIVq)IJPi}VN5>Z37+c?Y(7a>A5 zGR>r7y2e7|&qLPrthKNo?2Ps!c@p<2F6!ll~c!uka#O%|I%eQ00$0xx~=xx#NzmmnmEG>cN9oFAV4cxLpOpq)XrE{HYAxrXnyS<;J|4J>ol0!~3CvG=YBoCL zA|dta1;0RJZV~CT8LpuN z%Gq1^BnjN$Ad`i{7;|`Rd1%2jm%~(|l9M^lDy94R1{?5*5zG|bGnshI2F!#N?`)MV z{|lBt^aff{5;%Z4m-vgM8o~H?7@6Jxf6$IygqUHB7!|5bRQBcxK6$$P(0EKOXn{|L z#0yq-fV%-4z?|F88&60U5dOyhbo1s)0w_Ou8SYDTie~v&YETh7Gf4@wR6w+%1Mo1(r2mS2gG|SiFx*m@> z9C)BQ#6o5;Cf^>C%CF|?z;@DKa9hQE*=~gb3;wK@)+#Tqr?mSBc(=vi0vQNF!viL& zEW-l~sAXxi?tT`fbp{?+AMUw@OdN&fhQ@eT#jz92+*zh*Ug4k^?@yr=e=WfA4n5Qh zIknc`Iw~8i{~ZDjQ{Z)D)dmD_KUfACh<8kOHOOj>BU3WU0E#{$j+%mpYTNwG+yUHf zb4~|8!K2dJL0SZ7tl!3thYuGILN$yf8pz6eS-c<|yth>;(}SVR=vJN_1Gn34R4I{3 z%}Tt|)<8Q6b_ah^Lq)aYe>FAD&(ZWA9MGF-7k!s6B~mBjeSw6&}UdO*V4Bqk}Y$YTNoiJ%;SxpxVw!%yg3M~DX-4b z^7?te%aN8>;?ZFuf9lxs=gEp^y~u`OOrn#4x{Z-s{=UKdcDDRTQe0ENs|cQQLo?s4 z!bn6-OQloQlbWuJx{mLw0Cog>U;(AJQ-`pdR_i$g`?IBoGIR~xTBjIvs~H@M-D&)qR{e>LMU*rIv_MUoh&1s1a1 zs~X_ed3=wK`du#KnR(SSpZ$mUK%XrB%ORL!0zqpD-X}&)T%Vx!!cYDz89GpJIO^TI z(7K z({}CUo9Py95T6g_h=Vc%IudHg=ReYm1-9}jS}9TOx8 zPp#h53isKWHk@bcM0T0Myn9IHn>;E@n^Now-ZPfVo{h?}WXf$_vmc=(X>>ymb`t5h z^H3>Ve;1N90~l8F%5H)>u?D+~^Ig%!j-Ub8M#a=8p}1!YyVJAitn;y>bqx-}7MtTJ zF|M^=tU1tWeNfe~cX(PPRVdOH-&+^sU1BK<1ZC7m0^| z(sCto5%!+N64Zc>SGD~wD}m9WpwlX&c>{=_Ekw$9!T;)bMAj)_&1w+$*QLJDcW+Lt zLEfjT(?XmBU<+aHB9m={GBU7y^Tp9h!%^JT4Y|i5jj0X_aUiRyiVs!;8AJ62qQxW> zf5UMsl{+sHQ(fVQMPXN;hZqS&^IaEUY`Zg1em--EA^wTP4h*xYtRWWVw$K7TTMJ_SdT5dYJ+xetWeIo@1}pve`JJiB}bxSw4Uu9_XJ{VhMV%14ry00RZIwfGKR@S(68lpCe-!#d z*D&iDzMlNIW>Mwb4&Xe$n zN!Wr!_JWJH836711f z`Ul%e@bj6~b2!hnv6-e~5C8k`!LWvdfvh%9<7`V$u2zZ_^RnW-(;v)Dtbd9@qM17$ zo##C{zEyXv%NI#5a^KvUQnC7XuKp+2@*k~VfUAUl$f6Agel?&QR} z+&X%3<9`1~=p;8VPtjo4{6!J8w3wm@l;5JNd%RPX&{1C=EBt&if+t(}y%N`I7C!aD zOGBZo@EhvEM|eW`tMHjmY=aT>&mBDIaF*G>FH~C2HNl@Kkx>tbG=G!1oBG}KYyJ5VjS70J!sq_^#Nzj}6_tManEpeswu z5x0SV)HXNK2JP>O?SHX0!%9UBWCPk_tAVdPN>e;`Mz4p-wZ56aeP+|UUH~di=dce3zb9lt#WNfZA`7k0I!|{bXwZ0%#mb1o#)9GTQsc=TpY_#AE$O5Zq9{^;|-q^ z+*i>|a15BLot^RDVFybS>P+Mme1xUwHn2h>!%tom+W0)NkP@R@1vdkOoC!wg0%JD z0t7per;HHgL!bA@1+rJka~@6)I|-RF!Oy^rs|#Vy#|Eo2(yDK2pyG{>h`Pq!v={o| zGiE!uqJLGYCs(aSLh`NI;+y5XKr0_7HTNW z2Y9T^%dRS>w@u1hrJz#VswiF%10R9GYJ-SL_kRb;^M_-WLiMC(PU#5uY8_Te*0-kS z25V$y!n#CTnuoSPVN*(j_^e+PTDtpMku!MH>c`ozid~1ZEJnpEI_e>kE#s8(WTtFA zT3E$i5lc32f>d_O_{q^fgOqj}&Sc6sCTIN7BmPv97*)(9*5vGF4z@1xu8nN2y^Fil~KiTcsB#|II!$X8f(@ zj=KIv^M?J1IfD%?Reb@NLzP_A{rycjkpSajqs0@L`$>ZDyDvp2K_vO7m`fZ8fH~<4 zX0}6M3SB>BPfY7;%beLheYJu54yRwQerI>oKyf|2@pwTYfl{4N zLesvf?wG3F^B?r)HdZ~2%kiAKtFRF*kTt0qg6w{VlBqQQBu&TJi8zUuA~jNfZ~XZW-?_r|k2m@! zpL`XwA^WoKnsBhw&&5-B9dQPtAe{i`c^EiO&__MrfZ)pIug_(B78hu}k@>DyY09+A zOXiSqR;~{*an>g5)PI3Fm(!<1GsZYYSc{ofc2%VRds~t|;Aa6cURB*p>aM2?N83eJ zOwn!i>;HE2o_DYHepuc$&>8H#+Np=VES=(q-pdpml5kIoK|MNcehqqp#s`}c*$?NF z@byYmEq@(a4R8N@TAk5e%xnG1RZiaq&5!XQ3>*FClGe&rZGWC{RR$O2nml~I>vDR= z2dR1-+djI#qD#oHe@fL?H=)+$#Zhd{e z8$D2X*S+wn%uWI-c5x>2(OMr)JhATe!NI8kKi0+uG%gxNNjK&7QIv z`>Cv=xT%@<5w+L6k^k#gkAa`>&?g^@kN1B)&Jx()60Rr|^V7$Bd0t*{_^C6%T(bbb}oK;}|&n`*C>MOM=k)=#G$m=E3faF7K&e}67lr=U**tRd9Mc5N3LA4VWC z8(pN)#5kF@D-iv~w2)~c9w*j?J@6UZnOfV`m4hAubdrYDGs8b%_x4=S-8>I1TYuV>BFahXb?V-og*@gGgS#x(iPbXR zq!!%rwH?B}3eDnUNoWP4I&a+WxE9j6h~pFFw|x5ts9q+n<3pb`-RJ+|dmyW3g@^;^ zst)Xx(-`bh)#-i7E4WWoCv6-5U|wb*gO7O6%P#g}Tw0MXPQR3FlA{9F(=v8kbs?e! z*nd4~j)y*8Efr!y4gcLyqiwQoR)mRL;5Cs@B?N@{-`IyFW0nJe1@V$Ly^r8xc-VmE z0_?%^0zr(%^2CP9N~)H;pced4mTbo#b|7@^Y3jKE9 z3cAeg?T_)ILz9(8)2K=t`akU?!lDTg4S$_wa&XICpv@zI0lvV_$M`(-?rZ}Mg%zG^ zouqN1%D{W7U>!M8D_C44dyEDRa8RFmOKjXq^tS3@%tG6YQ7o5kb=$8_1QHliJ@?)n z#RIU+8}CrQ+RpIj$RWS_%StGy?&b8;r_p(<{?H)oG!q?3Ay3|UJ(ctG^vmePynkNX z%E}FeoX~yx2w~&3baXqq-$m9@0_Q*79>k2!J{OV65dQTIP{kg~q-X(!8RYsx4xzej zyML=q3eqfhoE0#xNxk9A*Oz{!A-;OvR>E6Ng|GBYJ3RUM3cgGSuQOvvi?1ST{sX=&2Kk-AmImT0BcU zBRZ!lGgjF{EN(X5=KFHtx*Y>SEQ6hxqUwnKZ36C=VFAO)M!3d+g1_crkLgG}!0p$FvM%h|p6FcG{vU6~2_^co=N!GGNQW`uXlYV1s>?UgJEy9;TrhZ#4C{Ky1?UURF#B#WRj zhWFws0F>Xb-q-U1X8GVHZE18f{kJ?gEReoIwCq~4tkNQ7sW{-5nbckdbX|H*)qI|9 z&v;K}KI0ef0%C@nhotwN#|*59?~vSq#2LzUWtw`kUT7_im86>9E`RTj05?mM2Xt}W zi1EUe*hu{=nkgW zrA^}wS?h298pvOFDaFh1;M}$&;wq)({%}(IiP||$qJJ!MQh(5iko9LX5r`w@q zw)mxxIDc@sCf}BA;Nf(lAYwVI4Nn5UpX28U44BXOaV8umzZd(^1fzJ~si4FJbg9)|y#w^3Vn$7k_)G!n-P zh5xaN_H_8o5M7z6L-=nIe`=QgQ`6`oipS>U4uALLlAMgBm`dwsY$~3`)p{DrX8w6C zV`>!g1Z4ixNmex3=UoXCvCvb8{dl?bmKkg=xChFYo#w!j_|guv@A64J*hDpO{LhHcb`{48+r%YR11rR2mgF^6&F00`d9gC`KklQX{3Zzb>aT3%WYB7MsRdrViRTm)k7YK&e(?yc7yd?yKT|5=iGScAc!qncbpoacc0nAeNg^)IxCjmY&^~M zJ&ZYH1ZIs)RzEz=ca~|V(XTcHEsqy$t;*_66FIvQ*fZcb6=4G_%2`@JvX36IrP2gB~J zec(T5C_XdrM;BeI6=aj~epJy-nWq0M!HqLTtX`rguR+zg@Zf|m$6f34 zO@`~lV3SXbY?BJitkqBB_)rA!1%LLcY&k(Jd0u)taBIBeaK34o+^y~QvV2p7h`!l-Hb;87F` zT=CjnY0hFsGnZ3@LVV<1{-L?)&9Q;An4?@#Sc(QRfb3@1HQdb}^)Xc`=2Zw z+$AR<(OF!NbiO{Nwd+`Eh=2Y;^U)zx&DRd+JXRy>;p(3Piw4yQ+tWDvD(1pxRw=L* z@<#+^guE*+Z5m)l+A=mDFJKWA@Z|AT(N%_l6nG!C0sJYJmHEME%v*CK(+hnS6dt%3 zWTh;DY*agr5bD`7ZfDYAAMrcr;xY>K!96tLGi)(6Odn8;v*t>&iX6q1-8Obvb~6pk8is(y-r02w;pEf~G6kzjuYt621PMOwdBH3`S+)+Oke`{}o=iFE_m!pU@L=3htxrA0708kKac&y7 zlo$5^Qm%{KQ*`jETN&+EZckAbWFeJTT;H691)6v)QbJ@Q+mHDQT^fHlo%9G3{uqU} zJ|8NYDASHL19aBrB++KA;Gk2<+<~f1EX!>^6WdB3zkk=-ADAdywbE~4E>#=t34CbhhuU%!6(rSOn8vGiL~pyp1L^g$bMAqM&x8X+?SFqT znSY9UKAzqZF~;o@3pI6M{qLhon>e@xia9m4xT0emVn(H5C;gy#2HUBP11XT>t5NQ1 ze#hAGynf|&lB(f3$UE!{RuV4XwGmTT@gvVln$cI9HPU&`epkcQhfAz-gp3l^sA7RI zWJnD-N8bA+YIbR*kEW^87Le&;R!XBux(larMyQn?ntd_=G5cW%oT6rZ6j`j$Oj$Uc@vVYey ztaCQ%aLI+RYBt&@JvE;Ub~-NA(jyj&Vke+rA|`yuz<&MBQtwp4%o-PtK>FCUF8Z$# z%{G6$zf@?QK>_cYpq!Jzb!Uw7-rxS+$Y#^$e#EsYFFndk~Pb2!HMMEk!TZhf6Ts9>)IVNN{d6p=0Fn-Zg2TOJcFe ze0@tHf1SE(XHINpmM88zq}dB}=)>x^LhjRVkb_8-2QL44*QoeR0Y`QRC{c^tSb9m7 zl3Ju2L`tQ5>1OE^>0Fv6mRNdu{r=wTeP{lhYtA*#InOlFF*JDYZ-8nYThefTd(#59}p#cQyE<;i{Oh%iMVV*LQ8EFpo!KZiP>l z527O8lT_8?t2Fo;Fkw|6ouLa<>u=i7`$>i`Y&rsDV)^p1OBA|M-~9i)0EmhSbdcdv zd_C%e8cX=Sa_$o^Zh!4J?vWuMwawuFG-%nHSa5abE&G#KpY>JCGkrRyc*2vRTa3j{ zh$FqYxrQnVFWcAKka3(-G?tuA zYL~ci?u0WISF{{ga;z3#YtMXnKKumlWJNd#2L^S1wx<5FOMm}T1?IOA;E}@~JxTrg z{v*C#W!nSK4H5PE@H3XV(qpS!j()H0e&P@N6ays0#B*QZB2`Yvd4lCAFrzsNY<=vDRiYs7L$=D~*saasb`hM3f|^Fn|X{;)ieHgmr3wSH>#LuP%~S3qP%(L5-Fo=oZ9=6>Es zBsjc)DN2lF|6}!oJquyc&4u?FJ#Z8~Qx~Mz+YQ=cGk>Rqr^6p!lrzU+ETiu?04+nx z`l4+w-lL}9(BIs>eZU4<;_f((swx-LlV48ZGh*0OwDnf)TvRX6J8QsgDnIr@ZGPPt zjVW=Yf&wbb%LOJrbQZU^PBoq#TO*!<9BjW0PYffYMDc@IpHKhi0wZv@=BK?L!ziws z*&}35wtsvS#ZRNegU(p?4^FRk#ktGV&Y@FO$6@Ivrr&ONS61XS^oZ<>kAFBkIUzEIx&NvGbiZVTIJG7eGHyLP z8&U5CY5z^Pfk{>)^y%qMH@KQWOxAZEz_eAeR7IK#vrwof0jE0)eM|hIYt1XJ; zgf!IEo|&`I2VK8vUob-G4l-l}{)poW@_XyQJo_aAL(5a{Uq6^;@`7$Ti|`GNI)PJ? z-+wvlkd%D~&9`vx<+S94Kd2%*siDbppe;Nfeu%I~JQ$4T#j|rMof2dzY~34&o*ywIFo>Y zfW)P6p!5y#Jkwm{wxSN!HP!4YB{}s!g!ov^4Y3AxfaJW1+x>IgTtKxK$bFL*jb#9wHGL6X1jN<=hR2%LjiyNy?T^fF-Ur zT95v~i@HU3qY@H^@Ge7oiHMGWZGWz<&h^nU#LTnWv1CogA!!IRY$=6bxJvn>^Wh&o zsA(HwMyj(Cq6n+!Jrh=)Oz^*H($yZm!be+TI6K5aPUU?YxpmtdGM7&{5g$Lu&%zqJs!M3Nv z^dsC;we$1y@cM#=9(fuMrzwe6O>ynO+n8y-aaBeLQIPlHtVJjFh1?wmcU0;USWSFC zw%Td$@6o2g0_!Y;asPtI4*80>LA~SC!zWVl4Ls;S8p;pdL0*o+^Y#G&#b}PcSXo>( zzoO56E{e@~#2%M+LR-5Uvwu=tT9Wgc5vRG;%qZ(J#L2rn-s|F$BF~BXVW`)v$jl>M zzL`Fuz_JBSP&rA}`js7UyM3mfapdwx-Cgza>ySy*o}kP9bb>I>RxDY;boky53l2Nq z@YrPRdz5?OwhG4{-d09<&p8k`yetjCTF<~eI@uy%R3ypa-Gy14%SBpQ<9|%VpHbrko+VG{gu5Z zr&(e%So3rL*JeeVCGbe}xT-_34)2tzL6`N(;PGEFSdmZuXn*#BF6KFjfR1{#o%h2O<`yDlop+OxYouku=goxP57d@QAv4`A4J6nOGsnoAznZ;C7wnWO>nht?E z>?ch7486~2x^@tGX|Hu(l&YC2Riwk;p>r%*2>E_DoOq}>IycwWT6c3ly%#8+FVo<> zILG}-mGF^>2Y)G()P(D>fZO&r4i>A;$Lwct>|^Y5YKq4W{yf1Boda-4Hh(@`5glJ@iZ>ZDk1hE3 z*n7jy_LTIZS9j{$ZnH$nHZx39ZJ8$Wo0py8HHh&6!+%zPJKZKPPajQ)O3JQK+NjL+ zg0nGzfuTmQMcO^LU2pN86xiQrVZ~UYSowKa{^wzn*bsOS?sT+qd(B6pR7Mu)+OtVQ zfMp?eNNimWfvNSLm+wk2r{~%3l9;d%x83v@%-w5bu5($o7nc=Ua;r&U`#!{@)`y+H zI&*+Hk$*0_q>Bo*F;1*C{}==yM82Z(A6sOFccnma*#Tay`!cS6)?S5zBu?&Ii==o5 zs~Bj0>|iI@|w~xtU!#7hXAAd$jQNG6y;taV~@TO1vhCYswjokv~ zY!?Evcet#-gXFn-@p>;^zN#)bf`eaN=)S$BLcOS{5$Yh&DFwg~g&Rkw3gI@c&zHjq zbp+U~TnTX6qpIoZO4pw@XX~^a%jwg#IN#|`Btxb?J~*ktw6wr$)qM?^iORBF*sj=p z5`UOMXs7gi;}%~&G#|Q50DbTz<1aQ;Er2CH?BPr7Pu+U^ai}^*B2uWNAN^9b#O8(A zyG-ktWAE*Y{@9??>an0>#Qyh$14p|DL*Jk<^p+~>Z{7o0*X2F7*fN;NxlfPSH#?Ro z+XLX5`Jl`DDDi3B?rx%W|8Fa=&-bEKoPU?8I;NI1#{ZVuOlZC$Dk?>AlfxQNTI!O0 zIc^jLTNfff0wkCZiY~tCq}x1k2LN)w>7Zy4;%OC0@U7Cuv)Fz*PGoR#Nmwg|ITIDb># zug#~}v22bms3U&(u%eI5NuMOe`%JK(ac5dxJD(TX#PfRwE&k9M&Us4_IfuL-kNmTP zO4qLan|@So&(+FN)YUN_uFp)rTeOyB-RFodwz&JsmPpG_5<(q?^h!La9+eeGVE~j% zSL+u7Zo0g|I08}tF^m6&nGddEi+`D2PtHZXLAuo0XyCZ#K)O(4@t4eU#Ak0po9*kF zi?tcHH|5#c&PKMCeKiL%cm64kHBRy0b`y!0&lU2}AxU;;yC2Qw>q;l+V(P_66~23& z&3%XadNJPo=d-^hduKJ%corcfwa`E-{)I5)eYEe!p@!Rxil~;@u0xqo27jFT-vlR< z1-LXEMe$^p$5}=vt=JA;Lw|=?XN>6|y5|`q_TWD4^nR)S1!a#XA+J0wS2-YE2!QGgM&9QKQ0NF&yc)W9DEGj+JYR4CnpnpC`z)w^hS$vGb zXH-gBmO*%Q#PcCVomf1_z=cnPw9<9DMdjRgYxE_*7uCDFDeg$c$w+D&wq#DZs?NK~ z3=47??#kBOABcU`8(pz$D^hBufIJPWxt`~afnQb;3jN54EHh^M+i4!gl=1po*B89k zf5401MD)r$lcO8$-+#n>HgZNa`tJXpSFb z1yNxU=(OGFthq-)mTmV%z}EeVljL~XqKV@t*FTA3o+n{t(zL1#8Wf27QtKRl@OA#Q ztW#Ls7Px5~#qKnkT6O$dw?gSnIRDv6C4!REV>aYUwqCl-pnskNz1TELY?D_oIYmv| zWubT&7<#*nigm)^uJ+0m=4~CVvK;}C{L5OCbdkYiz9i``H@W(lz9!X_WWz=`Rez2p zUR6K26a1!-n8rm{R-Z4UUq!5Ec)t%{Uqs$_iFRbGL2Vn4Nr?W;ctpT%YX)HTZ+Zx; zA8SsEDc*S^u773MK;z7pSw?I~dWc(%UF}=VGn-6ST}`uTlTxz%z!O6?;*_jh@Gb&+ z!I|UF`bhf7#?hhl+xRt~ffzoV$$WC}Xq9bZjo}Fd4v5honB!{Wm~$f%dGOAMJVmZ# zxk4NNL?^w?ZnC$+aYtyJiNtl2WbK8N=6tEwprGtRhkqQw@KYR*K%3Fsodr_6Fw!IMXG;m(Y+t>UUH;Ovvegn={m+ z*^ILP{#zfVv!p!637%7zZzRS;fPyk9HL^9-Hb*`N~qRq$XeE z2{2I?T$%ft@ok+;Nj2=DmDl$JGz~i>qa5l0@PA@&Ivf5K{rrZC7o$Rk9rJ^6xsrZ* zt&Dgwc9Qw$W>%b~yk*TGqkzhUe-QpL`)ne!D*6Wue;V{eOLo{on$gIL?YD7yuuCPt zcVr^7?(=^hlLf|5J4UYBShsY)fc4tg#LdV2Zu5}1&6vZ4S?v9?0=I=_nFt_pW2bX4v&idmoDP|m@3FR$$t-u zV5*W1*?f#-UKIv#0$K$D@N6)(w(>Ui`=V$~bm&7=#{ztI6&?By9BqRbXo((F=)LT6 zBs_MG@NPsUPY8G&5aUhg?&{Tu5HB!$yjQ*Z@{rmjutD}_c==}Rp_C}wpU2;4KmHS+ zMGX_6@#S#igzkH`-2lA$;q1J;NPkS-XxmLx+t`}M31GGj{Ot2oIEz2ke^p#XE-w-E zT&)2rfXezlayU~^pY8MEoO~CL6#tiXBFdk>fcdM}^T zCX`XYB2;dk9al#0uhs|d*nFIxAjtphVYZ;wEzM^l_df){Ag3~JWp|JAX@B%pSYyXj zQ!D-6O>IM=Kj3d7w8eqs*U^cBePGt#VJ@Ij4W7Rs1W9gELrP^<)s+`fOymTxj@Dj| zKvwh3i0Fx~#)lBAtTl|Jy<>WjSxRtEVR-hE;R1kXw_z_SCpmLsa9g5RC16sOPcmnd ze40*xpQbrlFFn{n#m_^so$~X8mp2OTJmX1MuDUX1D+x8RfOJY(zC6KY4UzI_2Mb%~ z(4T7V_>W!i)ZAUX^X9_ma=Xk8_*_^Xn;*|Ze(k?c5p;GOF7Z<#U_!DwnXT_m?CxG>+k3qRTsQjY+PNIen5Q944>>= zkp2x%Dqj|=Xf&0Nv46&LrTkz8mB{GUnF39^fK;n*Vq!zcXprByev08VqR2FLe#Z(< z{`Y8CYnCwyH6R+*YER2(wvkip9}=bcV`cj4p#T0G{@>0LM?+4Ma@7gkjBQXxenDlx zHMYIf2~{?0g~tf&8a-=9Jd%cAF6p5BfyRH^ z_h*k>_|51OnSOWO$@;lqjn1L#Fb+bBhp(QpKmOE&Ncf=;8UJ(Yr9m>Ui&aD8Ab(S1 zSF_MF}nf39>^+Ut@RTl;uDDQ+cWL> zliRmT2=PO*Ha}=(9BT|aG^ou)oV{q`3>9S+0UXsCLpi-ygxi92NPL{<3ryI zJ-fH~oNjMD_MV#A1xjJw%uzDgU({?DIKj#c9Df#en%0dZTjdDmfdxCMRDUWyW}wvR zu^@TL2YF%G5qAm=D}DmIR+BV}s!*H&`1f_$8gmiACM8LTY+$oZsDue>fH_n5u{v$p zSSH(j1Z$9r;PBzL?t{yfXy#ts4G|?TxWt#yjgpACwt|2fhvq#aL|p-QC!rTz%8d9TdF~7gN=1 zZSXO?jH{I$cluUCR-bgUR@e-kp?pO{&XMX9sMi-L#+gpHJzSkhWd)?45I;zno>3l;(v;a*crqKv(s zpMylnYx`K@sl4`I3C%qucIV`7Df2!8+wzL&cS6j;y))#fH}OP5iOcz@>qX7RELxJM zBWP0e%Bkf^{F2HiB|47Ucw5BV+Ft z+x$v-5Afa;M%ubDVsl3l>J&d46-xNjX(;h{jhSVTZX})Ug}!@yG2i=udVd`Ym=$|; zfFhP3iKg~vj_cIz&Mdo(NwKn4qu(GqeFazH9A0HQRzQaQxgN^YWcx7$Ub-F*w^5_t z8aqmB3fH_0OUH>#LkUWr-47OiJfMj=$k|-)1GC&IfX2mkN!}!~hTK z!JniHG54;cvi{?SB>%)00O4&tR5Q5&u&aJ%FshoyUs3#8-8+U0dzRJp^d@ ztA3b+CfqS~hYKQ_9{Wfc2Kyjc7$@5|XqPZi_Mf-Pgj@bjJQ+A~y3YP{F8cT6+Xs(G z4iR;KX%;_CIe401>V{)FtQl?g-U}LS3xoJ=OZgf1W~isBiooU8ev(CZ^Z#8> zSQJko8hD#rCQNTLk3@d+OhaWwK_ZCAoKeKLXvR^Ws@DUqMH zuK=g3Czq|HPA4OyihohBi{*c{^YSwrSD+L22-Uah&p*NbXt;yIu`IwqkI38 z#uqq!z#Lx9wk|484$`$5v-hJ!VBHp>Bqqn=!&0E#a`j8K(SKw0ABmIM)L8ubC{|Q) z8OmGgP(wOI)e>omk+^cdjYm~ ztUDz&GgtHPs`7T}d?0Wn+jZZ_IF&z!M<68UX1!>&`&WY5VtVb{JI$tq&XWXnnc58c zdT^cI?QZ5#jDL=?SZ2=BWj3rbW(e}8-YJJ;nz{OzO4UuXe3aYR!dTZ*- zE6R5|Y#{EL=@#Up=XI`GpvSQJ+P^KZdb;<`3iI_%ALZE^@~l`Vvfh$lzngyld33Yw z4e!Qp(3V5%$rarVVFFh0U@M?Q#Y9pxV1ujCx;La9s(;NgDPeO|8L{udm;hO zVyRLO-+u`8l#NB1wJ;;2Ms+cOgG;L%JDeWf@1`9E%yJ9pW>?20gx1(c4ar&0QE`3V_-~5#-`2$euMffS ze1E3fPePIdmZ?N*Q3#B*kBAb53f zq$^ym2wqsWjF?5s=YAC>ty9*k*%p9F>bSYj#`{!1tE16aX*pV>G*bXQa9vXG=vlHx z|7;jg20bI0dms*>=^9|6E30YZ2(x+KH-EV^O`5GPGcT|}6P6_uU!F|u>x>PlE*jOF ze|;(V6PRMD?r2&eYxpOWkL=5F)H?yMfVw_wE9H|ndBJz!#!HQ$+O0|y5-p|x=WARe zYTw{VseSKdgrq<5_FtkIjuRN(Wr50d1<#q$(taI6-M^76y^=0{B+$#sr^%0841Y~6 zX>fq}=vkt+`J6_)CTTBXe{{OIW?~Eq(e0UAfW*L}!`X^<*O5_Xv1@jff(qtkl#i~u zu)t`4JGg8sC7|$ft3%TV8}J+Y;#OoB>|Yg#Q#CaFZ;W=VyDr67Zx@YEwfKx*(BZPx z7&v;6%hl?m)3=izKI)Shq^Vnbqkke~=O>)N|L#y1FUuAmO}8~pC40KGDEI5;hwIaB zj@N7NL@ta4aqBnYr?1-AkS2o0_me56%$RBDg6x_j+7+fz&ue+se!Ehey}4C?q_bVn zImg!8>&vVj@YE%2GtN7=%g1&LoI_hqVT3`FpR1RuO0_VEO6*VOuT2V&hJUE;=?s27 zWSuQz6btUxIDYkALgOHT+^X)nJno0%JIER8nfwePu0o$BNks!ES;oF~2s<+*+%$t- zHH9-skjUIRV_}oihU751xZvDG0-zc#mbXdh=3*yR< zpRobtOpY6Ib1RW%=TK++9&;}S*0bK=&JTBWXWxN1dN9AQcf?%g3=zNhXH=79?=A(p zVuNn*R6oqNFko7zFDfZRn1qdWwBlsNYIdy-MLqvq?$T_kW>#d3^t-`ynnJ zPU@`RCLeiuE&NBG{EdyKE)XPs- z?DPY)hfYfs&kd)q`-Qg(#>@#`MP$!n@v`DNc2egom0xG)8Adq`AonZ1nBED+1zGQH9a@jP=O#vHAu(QGof z3$3Ptge*&emQ$N|csH|0CRmrUp z4_f z@-SlyD5{;`N=H|8kF?HK!%rGdfiRf08BOKW9PgFWd9ZZJH7Ll^ter_aNtVjFaR5^k z!gJcIVZHnN<$rJw3oPXmI1&G_d|QG1p_%wSw;1dDaSI*!=*E?1>0owRZD#yj!f3iY zv-EG&rp!x|FVrWJY->FhbBelB+v7M%!3r$0(W-kIPQL^hFl@J#bmrt!GkByzhT2 z|5LdNuYc}Uy}THO3&X-0V?a9EwiCa_-8Or}YIg?qJXV&@)tA1??HfX(Ja)iIQ%Lc( zVidIr&ZlVjn^@3M*a^Xohyv-S>2K)ZIHDk~-aewBf!7kx;B7KP!!`YBfkJ~@BaGmz z(N*RYn+uR^*;qak+fq{Ip9ZhV|7uI3_eYkQl7Cselc^NLJD<$nbF7#*h&vb;9d#jP z6e~-qyj1qeFsyb&@j3I&5+1vrA4fcQ#{Dffp$!Z3@NYUv!PlVa$cxFLysC2>0=1a=*y`iulP$Q+|%! zO@G}rPq46S_B%7?ESQ=Tb?6K|IB+%cIq-LDf=3A?#$EPSC}8XK8;J^gHBJa`>pySkQCy!t22nLBk)F4(cFGGV^gYqxDR)>EH2 zzm$K(?ln5v`Ip$0eV!CWFA@(;8g7JDO!GeHITU3fNBM#QxZU0{=9KNa%cV-@-L-_u06N1(j;FbekGN9%_923!9Y#; zJ2`e`n0qI7{?AsyLGFh@qd%o0Vv3rPHzvH9bkw6c%7%vY2LdkZeJ)9bB zrQL1AYe;yA;R^SD%3Zde(?czRaSc<)*Ky(c2>sY7Vfzi@{$1wl5D!7q#}bql3}t^Q zku2LI%0~c`EJNisoPQTRW?xu#A&E8r1PwHvXAG6yA&8zNRqWB@t^a^TF#%yXri*H6gIGxC}D zJWAp$NER}C;zC7vlIO9v_xDM2Dd>Mn#t&A46u-K@!&|XZrpe&_%%QbYX8SI{^_vA{ zW97&;3rsS06ltGuL~zzKPP?V!!?h8c=*<<5`E)^s`|k8K_A2~o9o={Fi5Gz7_UQUE zRj##4w)-F`T;u!t=GN;2Kd%M5 z%SfvMo?73u7}48h`<_A%Dd<{av}=x-+=9($)}&LCYU7R=^Y8;A=cf7EJ+0~pkKGR` zXL4G<+8GlckTNP2-b`S|`x}29q^x@m@EWS+8ihj>Nzv+NH zB;gZsWweb-Ogx_~)o*zTkoNC()^;@dWAFA&@Fl-eub#Dz^IMQT4&6f=^;6S0(Q<(y zqD9FP^UaI#xw_F`%9)LZLEFR0sp5C3gD&e$XX4NsvjM@?jvnCIl>>jdy30%jzhlaq zBIC#ndCc$|?rj8l6{Mac;k@(9{ZrO>lL=Zce=pChx-1jee(Jobz>W{vkZ%RO)B>q@ zkhj|JPblgXZ##=zBsOre*=oKDtFRvaT@ko=1vJ4fHa0fCZ8}4JsDZ34?j8~y;#P0R z*o;lG8v?wf`Uf#R0;GQc!Yz-q``DHA+7%??&zAWSeZq7>mq)-UC{f1ienjU)n)`vGAVy#MwYM_?_ahxS}}gY z?;7~5p1qy_OCiV93=}3G!$Qdxt`Xr33d8RTkqI?335%cx=2XOSkcbxc?~7ynrEsKj zh${U@#2wLXTSi1>mm6QjZ!}2p1+qER4OI~@0jsyMZXdd)%vLNesW}kM)0%L7+mTPX z48yaVB>N3jGnjw=i6lbT<}=~rSCKqWWN>j0-w~+?ABwEtBZ- zoX19dl$LM2w^PgJmm=~lA)G@k&TB05c=B#xqW$PMJcxhSgu*3)!d7Xa^mCw*a*&?5 z>R(2+UagO|GTIp)#+K$EB4jmZW}Y0R3=;cUu7 z5{Gl5-7kM<4%+D@Y%wMCXTPXGEQb^D9Qe_0v4Ap0IIss9#|BZotkpyoy_7_MDV=nV z8yP|UBXgOQ@ymkri{wO=q&3y>yNCqN&bB^gJ>|j9qdA9V!aea4;ZXd9J3Io?!9tC; zL=B#<0|Bq_^QG%opop-p=~gW3uzpj33|Wx~)P{eS5!A=A_dc)^Wj*yf$r|KTwF5O+ zwc>kg>X?BO@eI&(3&dVBtJZBPWKYkt-!p#w$=#4qZ<*(jF65e(5mX30+_(m;it9YV zb0%0{3t3{6j=yvFd|RfGIwEAXk6hE^|DcPg@J|9rY!teu>N>@?-tX>Gtvydg)rDk z`)eazSM4X~XUA3|f_0z(tjEQ6mr-upT{3?YNZR$iU8W>sqd=zki|ZL~72daPG^OxN zG)2tSzGlc3{vw)J-W2@9dC_O^;?2IDox#H5z|6)?56_(9w5}QdrjStvSxEBvIe1d= z@s7=wx#7{ujpK1E@NeNE<7lQ?VL300%|`~xH=N;C$dIw1-?lP;FFdW)E?q~tbdhCdcfwI%nKPTwP>jDfz3rgDIbPB_2>yuCW3W}$|Qb-Mtz5Z6e82z zMII(eFHZ}FlO2k=zHC22#?c(os4a8LSRt%UHxRp6VbGhWlO@zTuK0U3fb|IFL5=S6 zd%~X!fc)Pdo489R%InKJrwsnLU@U)J4tStjWG%72@`PUlLp?|ieJ4pbnF%OM*djan zrR4rx7w!O7dJSHx5T z)O5CQ;-WK_E68pZ8xKBWu?uFGjOcyQgI?CsRQU!#zF~W_Q-Zj}vlp|}pP`%D^ayG1 z&k?h#^JW#+Q^%borVKw{vV$5%JWS%HskjlRBX6TDa*wEY;*%?;O%lobo@nRyIgny~ zJ^Tq9cwLC_)Fg@|?N$^7$^m~DD7t5;ui}#&ENC%3K|*0%Hvrkd zPB`3*%;86wM%bV0DK(@Unz#dqqxHk43f^Raavp=2mj$(JxP!@Jh{MDKZ_DT6g!9?- zM??Okp7)Y4;ss5YnUJ(;r&c$caQu>%)HpT4xw>s!cQR z1Eue7CN*jJ(tvQ4{P#nq9{xfHg^BlVMvO!@K%ls)x+YQp*tBl?A8BHMTv=Ra8`#qJ z`{5;RFG1R9W`jAg+9)~{AERf-k9I=L)(h_T8K$iOAbtF?a_%tB{SDEopP-OIW6V_x83gRSXsEky{rOh)D*cr&)1mtUmAZfYV(?`c5t48IUZf$ zC+)1yI&+ut&px1i5^cCGD56WYHrnnAPIfol)C;_wgILVYeUP9psO zgmrwM1~Y!40)l@nl9$sMYBfjfNko4nB`RvP3`9Bq*f71(e;GcY^<8jhGg%JzzJ#J| z1s=`j>`!ZyL~tVFO^FmOkVGWUIq{bflKIfXu0R*jFTP8490i#_KdpZXP|>^7V*Rz+H7fV(#=L-K zD+Gd=-V^YwZGZL>q^7xrIW`T!8_6MQp7{DMl`itN9w~8o|H|$LHoqA4BwHjNkL{BVkSy*^xKT za%@CyQ)x%HNP&K{=T@>B66%E`~0 zf>aMh?zylvt9P3{zR|vWHLQ^{Qz;kL1?q&b1pGj$+jNPyVkSboGN&?gA5=x{wRd#Y zb?txGPPc;W-45+)`Sk&Tr+IC>YK_ajdrbQy0N@6#8Bn3=nDlg=NgygKrP$vj*b&?V5)y(n_3iFe{{H=B$U^<9dCUQT4pd8b7kc^ zU*NbZ;Muo&(r6WHWCoC9QpU`bHQG&Ky4jFTj`zO^5^ZE|OUz$0Z-@G$@a3G3Eg%(Y zb>T@>vHg7gdebjJdV_zwEd^Nn!kTiH_oWzOI^zbN#mY(%z#qMXMn{(fL;$(n;M?Vg825zRiPgr;tV^2#t9EoFJ0J1r}eeC16b z+f8`jXuxGSQP=0y&-dZF6L!sNynBCy3`o0D+HxAOC_Cy7qT&s!FbS6{kAE0-pOtq) zh$wgo0cKEnRH6(28X3q90Vv0W^)xnrma%^%n5Gv_ zxp6E^NM|?5k&TRy{XZ1dkakG_obb%n&cn83tUmcE%`VK6}z}@kQE{&4|e=$bcl2>`a8j`vXdSQ zqdD1X|63xTJWt%*EGNa2h39{mH7T$T;_tQlzi~N4tym$ua=J$w|23?(9FY=u?U(Fj zhXCt7QD2sp4k`pLLAiOajfi;3SYlHMs#VSsLScZQLIfg5RyyW1EwtbZm?*E&=w~&> z($9!bbmjvKtos#>Ubpge+Q<&I(2~KmgutT1?!iDmeY#33vuL$%K=*%mBRcQH-j8oN z($5&sXi1MhiN@%)PC8+p_LwhOlHS~z;D8aT`C+M%+Q5A!?)J^ly3R@fzVggH`fZj! zWq8D>-tn)~OjRA2YNmuo>p#|bC~C%PB3CDbU{cv(DKR!Qn`Sp@g{VNB$|UCee{oHE zOrG<~SafLmTpj%D%us&}T-j{)P~ViYBgwxC0~Ywd0e!YIqDlVO0auos<5x+=HG9QZ zV-ai!%B*%NcyYc*$EP!&g=4G1E;8KlayXgH4e$nS)Ao2dXx}e)7*3>`d-U{;PPq|b z&_x$P*xa-z)VRUiu>g#XNLGkjob+3S%ef^2i)YMjhIEX5I=p{|1F*Bs^Zj9!Sk9X2 z-|72#yfN}Mc@K?eOHp5EG>@g&4X@Pba6q4-GKwL??uag4rpt*+*ZnW_4W4WOO<%5K zmtCw}gpe?9L{N0MK^ckhF@KtE#2cV#>^NH|&1)YcKKVQ^QBik-rmY$gNZ&du8WN96 zv-#qMX|WUAU2}iRN~&yf-HF_9jS(C3Uo+4zP~Slx&e|C7hL}~!-_>hCqImYbti{KZ z9HgzI(1Sm38vXavxeqNfUy9g!bJUaPOqFeH%(2`|N@TD`L6)WdTjC^!DBTF+p z{T!?JPmTGm9VwwY9tP$ajurv@t#?OV>YXNcXC||773Y7kkRX--j~L8eUWT|GWE0;? zLyy12plJlo)^>(<>B_mCCHl+Huo>f|kQ2B}@-^qLQ<5{$;qP9?VNFvpk-r9vRf`+H zZRS`!;V0ff3<5vNAg{%K%481?F%Tgevr@k5?6gospCa*!ss5gv*%(WN!?I<{PAb6A zkdq4BKJzo#8|sd^bsHV#zPxZ)q~nFPbsCl$;N4o53Z zLzImeV$ad_DD9U)AE=I1b#t_&GHWJ9F4WBYi$=_tkvSp1@YxI*#ICb%v2O4EZQ)u^ z#9}jxA7z>ia}#zwc*>upl^!1WdsT}Bpa0k3TJnD_|EhjNbp96XI9rwN79W1%G3ldi zy}AXojrW{_xXzA|_0?1UL?@>`cn;{DOnuv#;_a;_VhVgRvZ|LFMA^G6ogEbW2oFBk zdvbwek8ZQcVNL-afTO!^7!hr;wDN{U8RD$1{|WfNnxC|KCt|Skze@(qZ^B=g#M&P@ zO2>ajLR2}JN+4c_faHL}9ZR!+El{qKAwO@Getyje6dse;1DezW7n!Y5(k=Bh297h4?IDy>UCMtF zu58jrX08V~|CC*e(`R5ZYr6I*tDwj<n?`J)b- zz<6e_{7y+Mp)Lf%G7r$-jCG=nUgT53iaYAkGLQG%<0|y`Wa+7&rVdo`2n#PjyyU6? zHZPSoHu+%eZKu@|IG8Fs!|3YIszARX*i@ito5w5#s7lRuJ^Z;BilD!SNXLJr+lw>% zlM7-y#KJ%0Zz(^VhbQSa@ap{hT-mO^Z`XzwJTr_Jll$U$cDY6RBEjQE`ufys3r0Pk z2zFMS#mpMd4$Rf4Ay*;Ol43cH1)nlZy7*Pp?~K7N{cvj9IS=J=(R{43daKjC*u3D3 zK&Swm$(>7y@gu%1EB1*FRgr%QFeJ z!br*Z%4m)l^iku;toRRSUd&tzTv})#ZE<$v<{rhrk)4p8Ei;Ju943FlU${@+JL#Q` z5!=UP)!%@I*~suSMP7FBs;_D~9mpB#EW|U9K+in*$Tm>GL>m7NPI8^~$!(u(I5O`h z+xp2r{dIz)WeHJ-vqi`k(I7Gu@DBgr5EV);uIyeueVVqp3cH*0F3p!HCA(`1L%4 z@_eSI6Q^gkHC8dTBqzw`M5Rk=s7sA%(^ek$ls`<}km|WAAmCp*9EP0v-!l4J_6x`> z1VXOa+X>F5o(O#ItfX72Jb%Cjl6$Vwpcvxf(-lf>hN&jZ%xOyZa|fR1D?4(YL`6xieTSiK0gFiUQSw}4T!%Puf7mMr|TIp$0i^dS%Sx$Fr4 zVr@nFA=MMSrbU1G4S7t@y5ieu?T@pC&mJ~d<4Bl>k_#+0g-qiBxVlP4s9 zbIE>@5Ob9lYepM=Ye}28r}doQwd|{!!qIGVAyv5_U+1e>C($vIjHL_7>xDPxux+e@ zxjDktP2y+mGD7NB_N%-b*O5L}Se1 zGN6A;vahSpm+zh##z~jB1YCa%R=we@JKQQj%r$wpaSN5(7wOf*zy7#4e>J1+NBT3T z+bou>$x=fsajWqvZb}N>?en2Q{&Acypzho@sJ<)|^fFrSPs<00Uwb>6luWb1Rbmjyqm$;%K4BTua6>U=> z`J$pq(CiBj@HpgP){V|F(_Lw8rI13$cK;^j7?Ro5^NyqQL%Np!vT*@G>$SUW$_RgV z?(2`~v`77L6UA`J7Z%on*-SijgV)^-)wRGZ@gjDCg%1&6GT&y$+0r|OdD8dTL9QiW= z#6FINg?S|~c*nu$?3z%09LM$0002M$Nklsr1@?or!0VHcV*R-{p zf!{n0EO9h)Y#rEOPvMFkU9l63gPq!GP6wyXSZT*{=q=%aQ8X8=9oLkXt+P`qMP8;H zh4um7k#`cBtF1vpgX!Jdhkt+b1U%CwV|~C^{CTI!pmKp$T&Kz{YHkkQ>Ldr$LC#YB zXXRi`{Gochqrv_1C5yEqW4`f18Gfqph4N>|8V2g&Wp6Kpg9qS2mmjh7D(%K)AmHtg zcul$hfbbZcXJ-J$@#8HnQjy6XSi_?FfJtc#RbwNKU6nR5Q#p}c>M4J1FhJhk?yL=D z+GH7@=g7fI^25u)5CU$VX_Mu$<$>)d;dAj1_IP$Y|8&@}ZjA=1QJBF$I1EKce$~W7 zZ3;VAlXV!$c3zrDT3qt*^_N}>zkBER;mtSS4nO?CUn-e`5FST@yFT3|<SO25>h*i{L|5q1g(TSg!m)^fn~Jfk z$$E_|c{1i;frc^Lq0W2p|E)Avhe6#EmCO*7q-Nhybj2}O*X?Y zOF*5pXeT*iQxt!qv^~n}{h{~yDUUeJ<=dEjrTTDB=gT^NrOti)YUO8sSC})1Af^zn zqt4Z4;0em%CK%{57^|dXZRWDbx7#Ss6vxN@)SL4b&tZk*Hdow8Ax+*b5AgsdClt(F z5g*kKW-m|-Fz!;;rbs9RGliY)j8$Ee4(a;*7lp&@WX^vO@rk(R!(yt8!Px0mjw>>{ zu<^uTr8%YV(uJ1b>gBJFYovLxya}$c9HluWJA&`b7T&58p`bGB5+&u6M{^5XNVG(A z=5UPjVm(6OYBYCP8O>FfC0td{1?6dNG>>#8|JfRd7kQ^3TaX0s5ErgsvVjGJd63KRnNrmm{B}%#FOv_{YZWiU&Q| zVH)QQESyK>7QBFi?@Ig;$KLn@9=YvYjZbuSVcS>!);}J!=imPLJGm&c6&fNlDK~|J zh-QsW6^md3hISF+!^A0!%7eIeL`JDQj0yrqJP3abDPbxm`v#K_owQYq!;}|F4JwTR zLaa2>jJpv~bhzLH=D32RcMOgz%#y{GT4fR#agAw3=FP!Xj0Y}RT#My!Tr=g!;)>9A zT%k>gK9R0yX1yE^qdX!`q`Bf}%K=?U(`im4iL{A>pp@pMIigO&gs{?bd|+^39@pX3 zaCLte_~g-?dL9c`6V6tTIW(`pA6c5)07zvaKk6G^VXzENG-y_h=$xXmQ@o?Hx{!+e zQR1CiXkO%Hb$alR;X_>(_=D6_nj4?j!XHY*>jNGD7ssc>=lT5Md_L1AOR}ZJ%c^nJ z4TEY1n+*6g{pMl>Mx4kLw)_0EcPe_?+G*z3yLLal9*wj&;0ziLHsV4uyCV<7tu zby~ZfBivm^cz(!7-r<3ItrjzIh9?~kKEP|63?R4_@{CrauW9go<$6!((9sOK zJ80CrpivXF1@=xSN;tLuEu{O z2WQ&km%E>YSK#wzDU2~r-J~54%a<(CWUxuMJhW0G*_&|y1X zM?RBr%cM9sa`>1X!uKNvP{ zTCcVe6)0n+y-13qO&d3wVeozX4upRrM?0l-WS3pJqEX@^99DT(ty(FYQ1@Qh6qN?U z;sbWB#xWml96MRu;EQd;FPl4eu3JX@4}`5-w`hmbXyfwdWlyT})M?4(yLJRr!qdhn zB{`TsjvC+CRdQ6qIURo>=Q;j+{^Di3;H2k9pXEPaZSb}xHSEB|=>3<$-MoL*7q0jA z*+i56n=vi@8T|?U96Y~7J3_lJT{b`Z7|L3w{tCs}Y$;9Wik~OLA16+pupM96%p?83 z_>1ppht>i+^4L(=Es0&Q<;#}_Y=JFZ+7UWCkK5%9>(;F+BjfJ78&((&;t6LaJ@;{x<5@PUs9 z;-pPDVc`wlqWuAb&cs2!umOX93Qn6t zJUPNMcVHaP6PVrFr?eSknPQ>{)3{2qn5do>@xX_6U%V_xyi9(ioyC7Oj-wJ>6+bOU z7FRC^xUx{-_!P^b_KzElYb*!28Xk)4Wn@kjX-+vJuCY9nL+uipD(ZngL0(&k;Ro8BqCZlz6!VbNCUk!e@wh( zSQOq9HmstAl!BDNu1Kj!cP*tLBCLpXcgNC=v^ak z`#jh6em!5#Irlwt&&-**4yx=f%D^_$%LpD-vH&NByzXL7_jmWuVKD$Jx^2SOVPTlx zj95gng4@}|d6vvjKpVsBp{Qe}e+XpKctots@JZj@Xkh0DENYQ6+&MA4Lu}1>@EGRN zxxl$KcV)iY(Yw3lS}kbFr5qAo_G3WfU6zn+THuAaZHB`{Zj)5Bio=PxYc&|IC*~kg ztmk1W7U3!%QB|Fb3aVL`K(3>yso>=y{|e|cN@irCGAjK0y-rigr;h2H3OvI6D&ZJERNQ0+IWt2H-2 zFBQoT^6i!b@wi~#rsa@~J`Wv)IHQ-aUX(+l)4xLhx?*-%5-&xP9B=O!zraL8<^OYK zJ;B&2S;YBa2z!|J2m_-WrPSfajEUoQYoZHR-8%^cWo<@{PKITuf5;zEbK=@1Qs9sZ zY;;zCkt4u+*EX5g9Ao}hFyzRg`Vv6*Jsu$$GQaY*e_Ea`qf8JY3sH9UVHr}GNG58r@ z@o=xZN&4Z@yOz_!pf8+YVHl#3G;g?kn(rnCZ1`&5(MW|52^>uq_gLW}5O*f{E`z1OyaJ z0*uM*%85zxe`*VN9Ze$%A;h(=y@aPd;t@M!cvW)v!`Cb@xA@P$VXfMFLWKkz}G!A%66sqz0)>iArGwi1|qpU z!tG67e;tz#zOh`tx7XzzU@Y|nhI@?9j4ep#<{9t&)}MOx)BX{ptZiT} zIBgNM)kKYN6wLy65Sc#CqNJZ=#P@Q%zhxZxe=5iXRlQ|}NjcRn`d0Q2I#u-fozDz} zreAsu%B|VxC)8^_K@h@8H&_HJ>}eW9B>sK14u%_}uf#1q&Em ze<6}M%Xhjz^jw(#7HWEA26^)>!n`C0Tt2NKdj>?}K@?O(+C3aE1#yIlV|O8dLrA?& zIvMtE(I0bTbawD)j;dgk3e@KLXHuYim0T!H%yTB6H6G2D!5Do=YCzWy(v1!4o^J6& z`Z+(+PMBqdfu8Qps}mxk%46tuaql!(e?fx42lRY7~Fxn!Ny8)+s%^ z$c%!~zdW``!bYFv&Rk1?bv|#0)8_lsb`r#8{RR&uT+c(|m#*N;@);53z|ItLmSOTK zaAi!g2%)}7saC1_#bddT#KWqpje9+eOOEEkTFef%B<>~(fdkwJW^ zp_p}H#(SmDgcMxvripD(rp0ndS9+4~2ye1Hl`AyWRa&3nWpgvVsV>B8yAXR@T~t6TQ)1WXzyVuhJ(f zN&7wjDI5Aart^hYhE#|5ojsRBV4TQV$^icgNx8LKuD9q8R&=sU_AQ}9W1HYvz_S+XrRz(bz| z)>}aZN#cM7vkFoj^BeV#$OIn4nq1Q8Vj@Y&GyTo!Hg-buJDGbVe=UEFb*2n0!L3$e@U|BCi$fkbtkrSKR~ zpN_mXj7Y{n7BY8SCPJI$*U@)*MS+7`aF|Rl3PnLzVg&HP`saeJ5U|8WsdOGVB7mDm z5Ch9drJtep6}(`pf4iUeb|6H&@@lW@%*MuHx{RNGX3#6ecKtg|O>O-QPYdy0$v1{Z zMW*0!ZY`SYyxFYTu9Q4hsIE3umFXjgz44=3{JgsY?Gx!320;$-mdu!X($*2SOzvT6 z@GLLYSAL#Mq6DW}R5$Q{@0-~$>wT29H?F0#Qj&5bb;Es5e?lsPWtTOrQ))>bYazAN zxF^<+b+_?zX(~~9${{_bW}o2l_bq~u^ZlhAAH^PJST%m((nO47nyvk`q7qqgF40n_ zHv2>0vXj)Ht+6aNreZvz#brKN&gw(D>4D#p-|o`Q-csm7b7exRBUlC-&9VmP56Gom zOrerZ#(0m+f6j7~3)2P0FiZBs;rJwD6TYCuE0;#AtT01Ki`J;*`=fB513%~Ak68Kj zmRLfYZR!?W&>jJ%1npIoc`9%p{qbA=LunC*U#spTdmqx7^wOo6?WgL%V`_^cp?e9e z{P%g~9*I=J+58n{g%p66E`zOt*>A&T8*Gs+W#3AEf5%geBJZF4zQk zuZZ-PWm+q|OnX@qxc=lm`p#3=Oy~D)a!+qu+Rik-h!iX9d<#3S%r4)h^L}>L0U4xk zH`tv%Ng&CCO!Sc#u#|qdFVZ?PxJ8r4`dXUScNUb`LuC-nRVAAr+1B3q4Q)##sEx@U zSXNGBf3gf?TT42RW+8tWcE@40l5cM%lr*H0t&;7I#IN8Kxb)Q9GSHXEddjwR)J>mB zT0tHb|DoY0L(@fwblO!2-`o8{q9X_dqz+)2U;4Izv<1?CqB`y)($JCde-{h!2ZeQ* zgw}tVK3&-kPLnyfNx^{$xgrYlx?x+tNDj1NfBCy1d^vlCkqAy1FTlTiF0b>7S*HOI zR;%j%zK^Wd*q>sYp z^CD4gdz+b__eExy58MH;IAN1-amOX-%w;HE?mVp(O%+KKY8e^MX32#A>~iXVi1s&Y ze=;-h!PqOC9Fu*i0>ndi4vf?cA|mV4Bf1Zc1dq^OyaY0R_H{OCLo<+*A9Ytkpl%ag zCL(BILQ)UQ@Bq521;HDE{AhaK&+tAm4`N^r!t>!HDW_IeVIAiw4@-=Uuf%{h>wJ0o zyOLxwflzc862QsR**OpHT=fd-*#H{9f61Ht&|4l#l_1K59IB}79)eU_BYEr)c^?p} zHTGGOay4Op6=5&+gp4y8F@r?>STFD=t-9XNT7f9@P?CtNdfON||k|_%k2AvbuZODviMrJ`E06f3MU1 z5-&R=?Q6*T@mI_VVny_?0WzyXQk3_n#MDwiJe41rRy6|24pj1Rht~Mmv-(zD9Uf^Z zS|9WXNb(iInON1tEG%J~f1!!3`x!HKdBFUa?*Xh~ zZ+Y0Be#Yd{Ck6KXT(69OO(AYre`Tj>3@tHKY&EE|G2M((c>+6RK;=$bF+5Tl$k3|k zbs^rsOd?5w0+K8Mb$!Q+=^ztrBOl%LJKW59BbQwEA`z}5BMROtd$EoPhkgwbtU-m3 z#rc2uVvVl=GSo7Gc!9&%rV2}o&Q%=%?+)T4V&n5=c0y{;1Qw}H8QFbyf96KUb+QXt zK=!EE+~bL>Fx1TTlqpd$s@eHP=k#%Xj2hZj0Rm*9Pu#4KWkYj}ogC%wJNTSD=JU)u zx&|Pb`htqGD$iz)B=tQ7B3Mpbi2%RDfv8BKE-qePl$34>#MIzXzmLQfIiu(%@e$#r z>^8Wg(AIU{7x;Iom| z4@#-71De;N1P5Z1f(mZ+CttC|mh3T%A!z&=gUN;TFww5rO>a0krq~6vb^LEJX>9C( zpRn5mB2^U~f_bhN?#fhoQYL29vSDVKZF=N)^`T`8KVR_>^-6Sce`INF9r4fD*|V42 zRHUWr{?F0-w^hB9M@ltquQ!^ZvuD zc^Jwk@e<-2r1sC_(y&L7DCD(JP?t~FnRin80R$B&u|Us&e^gBM{Y569t|n@f95Fkq zD~Gi|(j|4QfQqfVIlzraN0o%MN1h!Gxwh01=S<(p6FswunHWxCX_VttiQ0XBxk7pH zu#bN-DwQvq-dx7P8Vxmp^^!aD>ec0!CO)(!W=c3D-4Oa9leZ*o^nXt;IkthOTu$>k zW(w>Kc}4!ee5-k z94=pQjkbLF4SV9cK*{Gw8$zgv(NUf|GM^mHjiRNfc>$i8`!Pv?AZWBmKw8MZ9F6mkNr*Wx9UAL z9!ZAW*OP3{+6;iT4WIOZTu(;(Z80GJEAc&R4eN9%Xk^!c6x|>302Wam@f3fgtUl|m zygmt|e}Ul`(;EV^Oj`M=YR1}`inrt;n)C_ARhvCJRewhOby|HIPsj0VOd4t8)jgg& zIu;?MViqEzCahPNF`RFH5@3LuP0m>iBI=gIza!lF5%aw{JWR$==7`H3E7eNH1%sqq zFcWNpzBaZQ0rBaPD^#zj?uFE<*_QuNra~~mfRR4+Hx$$9*!EQ^Z=4ta zfAG(Qx9Y-8&T+rH4_^|xrOfkw&P8K{@Ln5JVuo-GWKl|R=_gc3Y?68{Dr@fxyQ%K% zX;Dl}X8vZ=KWs{rDY$7$gKSK$KT-togPJ0F8$vX+$lRpnH!29e ze)@`0Xcu1ir;P)Vg^_ewTzq3`Td{q0Xn&fo@XQ_k2~MJZV3lzTXv@O|^vsAqe{hJ1 zt4A`8S4w$t1ggxG%uU@BLxNK?V~RkZkF12Qc(aPW0Eqj3PL6)_BTTC|wnEc=;Bv9h zY?UOAHNY z{?kFovc_S?j_8#AHs7VNsHh4%e;fq^!yS}j=!&>cM0>*Jg(B1brLIXJ-LEpM^^vTw zlb{=yGkV*lavyWP)xlh*%*VJqKC_rF5rHurY)T$VePW=d=n?DOu*TDd>c^LF=Pkr6 zUH`m%+rrIFj93Za(tp zC9^etI6n4|{JtY;n&$rW$)Dm*^9E|VWDzBI_xJ$P9gvgB-VetXI*a?QS|L*xvs-e0 z-%m6=Sh*DQVcX9mx636Sf7au_IQ|rw-p5?8Zs}?n3Uf^Nu5olXSJ3qETlZs6@A%YZ z?RF(Ls~P7nv9>KfBRD0{tlBCBWp!G)=DnzQqBFGqyal?*Z#8=MIM>*EpBj*Yt*rdQPZTT*V$s*b)h7hZN$)fBl&{L^k{REgk&S zIU_ynx0Z$Qd}AL0JOczX<)U}Y68nz?Qq=;s1o+0hZ@x8)oT|jg0Wcsw1OnSn#5&m7 z$LPO>+I@-QTekK8dT^d++J$fGi?xQ1`6ga2zX!GC-8-PioF4Mw8{Y;+1{3t`LT>mo zDS`Ziwl6wSxORavf3hk7l#An~6fZvVgIi2-OCB39lJ~Qs41Y=30~FhFfgc&xeLs@% z^Nfa~kCkbC6_=tLSYd{Lhy-TZc6qqc3AgzTD=Bwue#4jNzCF-|ANbNss&pZ%9iwtm zU*)Itu+{&E)pYQkN5YE^$NSKYy2=Z3UAyFLR<1vyPm<%Ze>17+mF`@@wY2)iLqiT|}S{j=p) za%5W}qhLezK2?rycDAr%!XtX^mOyXmwpbEc zZy7k(%N4QBe^dpxaDMNVGT<`bu~i7fpYXlg_C024=5-vJGLveGH34IL^KSDgA?<{W z3ijrdn7}pnrf)fYCAI15=XxBI0k{J=PWkDVj1cUcRiBca>2Cap@Cx>I(8!+vA#IR& zoz)quE$q!99USylds!C*PD7sbq2@RVc+-E&5KeOnf1;oHjMc6lu|~>76o5OD#fI_b zn{;_wIvO6gBs_PB&O;5;iGcoW^RJ@H4??*kg(f{8_!-1XI3yw*=PpTr4wH`GVm(augG+f$v$q`6BErKH{H#h zKU4wp`1sTuyMbiYOIOHs+My*zrQCz zdkFoGDJ`vje4^PmJad1B2K_0KpK2VYe--qcdFT6zPAkXGmc^}niz!leqDsw75PHPh zp^!B4iHErBP*Zz0!RC?+}quR1T=f$1z8 zPu9i+%q-e?lel5;9?A0c04hM$zbl^1ppDMufqI(Yy@PB3gU+Yxj=G2Vle95g8uY{c z-G7$^z)f+tb7P8<&-^i5J`Zj*pGLmad&fyxv!7*{;X|k>KEkhpWm_y8F$nNk7NRS8 zZ0M(X6o&a195A{!Uw3{+Gp`nYER8)<>L?9efz<(QZ$AEHfJfyK99ZCgQ;D;kX53v% zT(Ti|L3Y!u*gXeka{p?M=ppOQT@o)@YkzNSEEz1<3PJ?^6Nn%8$B5m%jn7zK{8?cT zx<6i7^S%Nl8_59*%`=W0$e1IdmWp^llC}DahfLP4I=eZMp6%s??A_7eBPAUwg|qxo z|IX0@)#p7n-xytn+H3~ZXn%!RLR3XOny~BbN11mEsOtnCW$*lqvajeph_ufI1b=_~ zZ5+*2$IsGL_uO&as=z^-%HVQ4lUJ1Qbsa9tCxc7-;+HdzAvIjmP?+Uo(CF8uRp0w8 zdfKkcJS%FF{Tnc$V9#R)awxZnjRkgm0SwxDEcI5QM zauN~D(^MFCK*Ev)t!5eRmxw(Bjr^{KRUDq2&hAT(_1ZX8iB1+(3Xlx&w0~ZGGTcd_ z-{X$xy0_pP={ddNwZteg{<|FPeo}{q^1!G5p<$s!-C%Zio0%4Zv2| zNlrSOTm(Uk)NPsH-q}`QT7TR!-Y;`TIOY+y0S>oYy|q4-Lu+2DSwhaGu>KR4-d%DwSKnxYvcG?tlO0&lsp@mW zIbm8Pv@r5D6fpxL0>?y+lofqYD$yK`m=UC@eS^)q?={`~IsKV`2FHSEYQ5 zFA0J>0!ejxMNeLaSvA}b^L{RHy9_-0S}n?W(r;w=>RCa2XMf0@w@E9UOh5i{G?CAE zM2xq-zqZ?HFcRAq`&;giCGhZCUqFP@N9=>Lf>GNG>U+UlJPZU>rf=>Ij4SLg_i%XL zj~0e<`S20abZc%kS^Zu`%1yIb^hUWLCa`amS%Rdgf^Ej)`>xQZk0de;-$h12!f$M* zBub2e%zTsD+J8!wYa@a+{LR^acSJxd zzK6(joBcDfAzQ04v68c1*y?(^+-71TJV!{US_^^WrP=fDEr`dH`BYwE;miB_tB<&sXaSyK2n#x{8DJb zU$Qo^_B#~(h=cvC?M^;(=j%*>FYr+bgQ+3Cfu>?K*J+05I5mX&M7}JH^z_Fvt(?~L z@BJf6E`NN;cFm*AdbGmDk6}p6_T8b!ulU>P{wgKjzh`a@OTetcKbV!+852(hUOnam zW0n4V8&{el*0ved|8wCd{YQf1^u<3ovc;JhmF8CSiTb&1;Tn_%6hqxo1_Y$hGMllT zlFM-0ft9w2^Ux+2f?V+`jVr+bfz|c9$B`)icYguL7FUmt&SftwD#WNMzvnFr&%2x| zbMPOBz>@A4o`m~ctbG$H4Q!A;I@9pXb1)l95k~RUd&m9Zuq3)4aV7{^eY21uqDEM& zU;X2t%*T!K!nI>W3illlcvhZK-;uB$PeA1G2b$<&&R7;q+#=PvZp}-n)HG}HCMC;+ zb$_5DJ>9(iXF-*Zh^Mp z!|rRXkEJbl65iuEqu=oN+Y{F2Ak~ODnLnf_h32XC0le7A1E9Rv=_$WT=8)RFoNwZ$ zS7OZeE$Y(GCF4J9LTlXOd59Sc)iyd(^_TE;hrMlc(mf;ZVnhGy zhXXDtFr0J=Ox7IHyPs1f*KfJ8lSnp4bQcDa#Wbub9Ih#kjX5}tMy3?hcm+l6C4W33 z3(OxGJ%8`vav0kB@aSEK-?tc5vYjPwbmJaErUe1ViwsN}7WT(C%Onx&lphgfP5vbL zdGZKNRYSU=*6c>+Ucu8U{w$alH-G%s{iRYx!lThQVv}P;LW;qcT_kAt`&Do4C*IVd zPjjVe)?e`uzncDww&7hp?9YXhgF+r=tPQ+k5<9vux!159CDoy4B&6nQVVG8gTp_}x z^%*@Um^l0Xs0$@d_G0IC1C9pUVY~nhW=HUtnI+SJjDPfA)<^2Y z$r}!}ysFHb_u5aU@&UF61v2vQ5fkBb%T(LyuM*jqlpE>*f0Z9<8&h-GQn%}78052Z zCywy63(tW6{z%F4H=_I&csIPptsxik)(tr|+YRCV;I^s#N1{iFQtDrZ5-?6eee>c= zA(DZgazrcqjI~55i%3=_tA`tG;ag^k+6Nu*l?M;*sXfIIWEk~Kyw zf|y@mEc}i43#Jq2i~xa9t+t_{G1lzJ*5Uw}4f2&{eyKq&#kx*&9r^6n(aO&}Aq{+A z+lm-OKhl$H0diRD(H(Bllm|wU@_gWA-qg+rYHh@~;I;=Z!d9B3(tl8AcRDi*p5&>Z z=EJpEDgskVnlxaav{w3GjlZP+CT82Ej5Aa6qJd1=j zr3TODq^oD&HKJ9H^9R)}D3c9h5Ag()peG`2^gdt%&&A`q-|d)l8;nkAWGe%RPIK8Q z=$6|j3T20YR@FYxSEiIYEW5uLySrWZui*AF37$E%mAJ9r5uKA;@D!Ir$dq?KseKG- zCN~4gpg7$wU4P-o)Si(F`gCgET&@HDYjYhqh9HR(eTDW%o%NK_8$0`52rHS+xeEbe ziXJk!$Z#E7&x!Ik+Ti7bEDwRy@Y9T=6qJhtLf9~gnR$)r(|%rZ0puP zI0B0BNq?F)h&D9#dG$rCewvNHK&H(9@`>4#kjK!01PYxDFo)XL{B-*C%w_n9+-Wbn zPjAad>84!^a8eE!j$vOh$VLpWz9;r9R#;R&=&2+0|K7{= zeYBcBTI3(k1!l&{EYD&LKeeu2$)(f*vSB+qWPj;qWkMz5KA(CXU0A$!KJ=x1%F29G zbJDybyC`Yb@I1T&Ep)!>op=dp0bp)qO#!^d5(pxhlBvEi?}kK()KEd26eU7lj{m<#X+oy0M(~KpgBG#kOru&F3>A`Dy z41Y4*Bn&+!^v{jp^zppV$a=897XZ@|iT`Mlo{dX=1yYWQ>sJdq++}cTnpl9zZal3Dotd-!G>({&H+y-T3#&EN{ zL~L&)cZ#}Kh|MbQb9h!fmt352%5v|bN`GeO8dlkjPifxWHJfQ>FT0MUur(hkn5(qv zJqxrJ`}^zlArYc-QSJQI3LhRV_HRFzHYu3D&plQquUxRAe4NH+>u{Bxm5KSE_yxk+#u?<|NDw$50jHQ z2t7@cdk3d1>5#%RXi7jywsEuo>wUbO>gP3UzauhNe#|doJ>y!)96Mo#6@Owe^NMox zHHYmv>{a)j3Oe=;ZfEoSNll%Gk+K#R4%l9A*-! zd7MlMJ>h&{)M$hLnf7#(E;R`Sh0abI#}2)8U+(7(=xEw-066sEpS|$~dFr(M6&pl1 z03#zC+-s&6hxtV`t?2Rl;(ulud8*r7bEsuMi3?TN4i4L(|7W#?&_XAilUWy|TPz>7km@ihmP0jX$*t_e+DejVp2 zxw-0El%2Juj7hjZQ5%pSaKn@^Gh5fWI2|zF??Jsc<|9~S;3IDgLx0;gHFcB$jtty@ zF@$PZ2TALKH15-XIq}2o3IV+o+tx$NMG`1dW@%ikJ(X?CixNQ;!AfbsB#IZ8#rCM= zp`|_3z_Xlc5`#fxD~Uv$KkA|X?)bwbPtMrkyds30qJNg=$J*mS@Hfobm#hH( z&yrkjm7l5L*Xx_={#W_U=sM}+P3jsq>nw+#4&@K9j20^Paw2j+^Wbo}OVpqE{%bRS zXQ952Ue&Zijty_4!l{gzeXOw~bza8PZCT{#nzIYbV~+bKezwt2lfOx8+IrQ{s4w;j z4*G|ubIhbNvwxG!?zkFHpw3mw!2(K>?Dj+Yhy2wll}oikc_Hv8>mcdo|5`+mMoBa2 z5@h5t?;XEZI?8RYV%hG!80+M{;FWXLwF>=F^^);j75B8zaN1A$Ft3z{=S+q21V`(1 z#uG!?8~2%xyVs}L?(Osm$6|xQ`t-9Lj(dilH|N*YZ-4PZK;i_&vc<$~^iW*It;f4s z-F+GF&x(hXy`CR~f@fqOy&WjlW4xZ;v$1>Fv0b2H=i2hu&#wUtKNDZ_OW}n;bV0pH z0_CbJzOlRkw!zfSG0>rVVyoZmG`MpWeho3yRUuX?}F=0YKW%aYT>Zai4g$kJ^0+TApqAg!k#l63@m#3ejn)}#?5MG7GHuGVIM1PoBEX6z;$rRv1#j3X1GXB zVfX$`hw7v+YH8-cNL2gikF+J$-g8T4x0)LLYEMwZZ;$d|+IpcfcMb*ArPAi|@w`xf z#ABLtg$#SO2nWhoXcr4#hIVH;ZeTBW18Sy3u4d1*9QF6`H{hu2n>;NYs80VsOJ7TP z$bU71OSwL_NA21lq&m)}>p5%ZryK-by=rv-H7VcaXl!2_m^a#h@t6@jNT)kwNpA7) zP}yD>=q1AP;ia8si2&gq`%S2u<@z`r;viw6rq^S?Zs}$%lLl99EaudD9mP9S3plHL zu#=w_Unhl5!T35fZNMB)LzZgh=oFv-bboIWqa4dyB5~U&1KlP{A2!F}>_x_$kT2x- z_joF0Za!B_AE%yrE|w0~#A!a3-|}C@e~=8iLBR`aoT`y6Qr#^1I5tQ<-fzK4v>IhY#5P{mt{E@jX3# zSU5>8`c0nAefEr(uQFIqtFu{CxIGJ6c^`hI9!A?uAQFxbbDmYwTbO(x>*e5EG*<#- zTk^#a*{q(l|K{Ldj1Tj<$!2ZU*?$|u5=L;{XFa&7b2XAKS9DWpgg>7 zRm`(9oXg{$^^F-^fp@%Vf&W~5XvX2!Flv=(ns3ZNS6Nyr>1^pCoNo2*raCbpbHsP9 zt+6JQlJ=3$2dogUWY8lZJ?QW0nl_pCL2L^Nmm1dK=0(I#5@;sqUPchvZ-1GuL;(y6 z4Eh~Ml&3_rc@KZZ6DNqX)mTInf+Y?zx`kQ;mp#AKF&xdi#m@xbrERR!o5$0Fj&cq) z=gA!}&we5iUruFiv0fb#5d93Amt?t?8?mGaiFG3qeVu^ z=Y(Se*S=Smi_9{_$-8byxjXSYv+mi4)l-vUbI;va@@^d|MWV)GDu3)FM3hdYOfQQI z7q$_MwY$V3KO5NIFcq|f+`W~4So%~_4-P!9wkHgM)ZQn*#O>`wHTWRZW}Zoym{;{F zeHqOxXUnZul0tDVrP==jxQf-B$0`4MY^oX3%0|{GQPm z3a1C_C+)I(oNg!PZYM6A*-U`%fRziM5a!{;PirdDS0Qu(rS&VOq<;T9=cY-dr*U4PB% zc(c%Ic`J3Tk6WqivGeb43yivsx^Jv9Y-ke{;B#!ce7(@PTWP%Du;guCalIN%G51{M zVRIyYt|WB*du^MYB43@fs=;qC-3N98Sj;dL7K>ScOKmF?p5kW<8+&Z8vdGn@Zv%ak z4G;Hc+qC`C?P|y$Hh&TdL$pCq_6@(Ys=KTPOKT(G?|*k@dEH2***(3rqpG#E=6nXM z7@zB$QMbRck6DD$`t5~l?|oE`*{&Y^tJGhkubi+qbNJVBX3kNNwxlYe;DsntC9@=K zx_7v_rl5Ri+p_t*axg$$CVOYO2kgX6qIyPvufmU`klsoP(kKc1F#@w(xO?Z_^*7&8h@Y)+qn1TgLhMT?SP$?T6xZ?#_tJ| zxk^Y@b4%V_?)y-ets>V^N!ty3QIVOqvDUM6JoJoCV%%Gx43S00A(2_Flm!!W_S2m# zA7@<~0#!a-&nUn?-z0U>=9Z&9L5t;&cEW5W0_V7BV~=y6c@x)~?Nkv5aIR71UX7b1 zSbv@RX;*kI@)#TYiRZn`UOBvhp~K#PnKF7feRFmck;T=Ob=W(|+iK$FB<4_yEU9{o zz)uVE*6JLDee*oht)QIEtXXgrDX5xQv=L4g3H&bnPUF{arKeuAN6M((Kls6#?Z?a) z-gCB+Qx4@Z-0WJ7*~1uBdYVbj$r?nOAAftlhAkvIGMeUmvL>fy?GJB+j&o-^@8ZpV zla8B6O6elngTmkcR7WzuazTtxR%9uK24NH~Xg8cd@iT?c!X*6cJ=NIH`0gAqMcXfE z3Rwy0+lt_wSm@mp_i&TtupPnA2`>df)Wh;bQq1Y}+8v!ef_)sz3O*zy-#!w51b-PQ z=dB)h_(N!>ST)}0$PyX+k?1Ltuk)3bH&}Tm{e1-dOYqD<3uW_|x@_+2qEL+nvx=T{JGAgY7Sp4mXd6RToWX*f@ zb`!dam6@k!!j#`I6&d`~nKpjlpMRoS1i>l2(zD6uJbqIYQ_Y5la~Q|i?f|%%0;E)6 zz-+ghcb&_3!F1kl4u(tHbohpTh=eJU8xgHh06`9nlg?Etiyq~wzaC#AfI(wD;QYxH zz7`wbuxzGwu@`eJ^G%7!2cb=4R_&R#Qbx5psv~j7XT(f@5m0r2-iWQ^&wqm*Nd#wV zB#}m*f883XO8;OE&9z%>iI=rnQ9{q~8NbA_x)H5YO{kK@gy&6MvJ~<}Ir^YQdP5mV zRL>kUWRV#tIt}WZ4LiSkQ<@sF%$a~VKKc_GXWP^U04l}gg46fg+nvR|+xF`>K*cjX zbNz7}585@ zdA2YQkE#K!=$R)%Rce0>HHc3ae=R=SEu5@_$cJpGN(4qMN3qVCicENKxn*uZa=|Uv zmliRY3<7~3qoMMCJi#)&S zI#RqWaJXJ9Sn*3~<4p^|@3d$e4DgRVxZ(oj=nYR&5Gqh)& z0@+(%pXD3=3kyM)n`)9TDj#mdIG5+#R(0QaYS$X-FFFCe^MCuCS}mXxO|uq2!)C-V zAV=Sm3tTaYsoenDS2zQ=YLq#JL_{5H?EwqUOS5C9Q@jke_@Et+%Y8aCJHsOu4@Dl=~eOR(Wad3W*X zYuab|>F-{8?SHEWuGf^B|6X{;U2qVv{@`-H{HLe-4F5{)fRmn)buqZvCPCWsqu-&c zrX+J96_${?&guHg{g;9XFC)yKK( zb$fJ)YlA9#u6KCgxV&KHuoQhK{MFK}Vr68PdJ2_sXg#_0 zZ{NS_3x5?^>7enPvIg15sPIwr4s3*@JG_ksWn^S9Qs_D zsE(h^4VIey;s~|Pt~O%7>w0OCtrWevpaZn$Id;MG^!H;Np7Njq+Na?-Ux=Iy2Yb$v z$NKGd{yB(-mwR5FHfN^Vj}LPVc+EXPp3atXJAc)d23a;=mzI3ehg@YWoV2Mm9yRGD zzmRGxSnFcB9NpIP!%tJ=^pxTZB~(I|L>J3sY>F#xjb?2Hhf8X&Wdgbus~hG@QVh(N z0<%YFJ?@z;j;gL|lUzY#Hqleb@l&%$d1+OeV9yy<4c}b_%KfUOj?*F%%WaugAIy=x zQ-5A_ykC`&%nL6;^!5oxAS5kp0x=P4zqjptFc$Nd)$y9NaSsnyn?`L zau}WFxja)C!nx56)$otmT%nyEtNP!@?#@U~>h>Mm)}Swu)2F5QY2k@fTsu*Xesh>R zX`|?@aKBp<^qKQBWDhm2yqo$LKb60WUVo&vy;F?gEx4Kg#PKHjXD{`yls{Ah9f8?o zIc`=dytJ0SxVhI2UCjP7`{O?!v1P13{o-SNXerBX^uy-+53(jc;f}ks&vV{0wj|vz z*&y?+V6k{)8OnL2{WXuDk3glRL@c_>LF(O9;T6BuOitlP-6leolw6kEiw(Z2YJUS+ zx)z5`X8*P+)3&IU{)j01kY<3pfkoSgzhb3ChhGX0NupDOgYAP7U1%0I9hYZSrH@x< zskiCTPp3LL*{=dm?*I*z?yK1tt#!tEa&l8wpoD=X6mAW;toqq#&ur<3D-!f@{0e*} zHFJc?0BrH!u#v$HJ=u`%^he%};D4|LhPF%W@D~@&W}!;*M9hEVNGajOj@r?f&QVUc zb19ks#*oG{h9!rABRrV(_Cv&64*2hP)7RZR{kA1o#iAPt%G8l*G?}2sBTP( zEY(4_e;lkfmrUj>ktH>6qv&Sq{z6t@E&d11l+%ATH}uW&qRM%K(gqTENq9-ME}c{tx)D<8C+u(M6BPjR*UBZe z4g8CnOZ4IykdJPx!wAX)f)GXl6P8-yMKqDl^XDUx`j;tyx&K{9Q~Yg zVJG+f5P>J3mm|+3RM^T?et+-y?gbM8Y9b=)F$teDRFjz4tu$SI7mjdf+_t^*v=Os) zi~ha3i2BoZ&ylxlg)nqf>e1ma<^drh?YU>TMFVpuK7_Q8zF;?|M?*8q>!|*?+`6RgwfEi7eyE z2!M3*YvXKcRb@6h37>0BilW^cS4Yhq^(Ob6xr?*Vm@YDpqvJb#Nl(SM}}rj>D}KqIP6 ziEByNjJCFO?SRo<*{zResG+F&Z|;|d_M-ER6M?xc{Uwu`&AdT&aWQ+2wb0Vinovhg z+#)!|`4n7KDVa8735=EP>Q|b%mI-Pxbi4e}cwry8>*bVaoqS+F?np3Eb3{F`*Oj}m z%bH`p+c#hzhkvBkzb;kvyu}Q8Za{zHcx#8sbFzGQ3nfLTIgJJUu4eghXZ9LIET%5Z!98OHAb6{(r?%mE`TtL_b=@KJhlOy0*p6 zxcTza<*}i0m7efT9QgvSvi0Um^W`w1{2h&u)s;p8e5z zKg#pS`hRax=HCw;rqyb;;XKNTCN!rBbG$)Uez-y1R)-DflFjWpXTr4h{*6L1i$Uox z+%%FDR(PX!wb0mcvwC@p!o*BFv}VQ^F&jGPJON!yDg5La28SWIoeP8nK?RSbPqMxqm*pXDg`5R0H*hEK+?6MZlB?)V+s| z1*XypCC!>+IpMM%s3nalI2%bG}ynx_LlxNb7t<<3?^rFK8M{nzv}Q zB7ae%c;%}FY`qzB8^Y}%x|jaiFLe0&1>*Q6)iro#{tq2*859Q-Y=K755Fn7?7Tn!E z!JQD?Ey&`wz@iDRi@OGgMS{a(K{vQB?y$J)qVKyuUe&u*_f?&LGgUJ^)7|HEjYNH- z?0cbAsyIfNCL(gdqXDP;IX4=Zu=PoF34fo?6!H3ThVdNh=A0Vbh0Erpxxn^e0k*=x zv<2xdzw%LbNr%~*c3btMy{6l=0oX*dftGC4zf>8Ukr}NzZd~A;6kH~em*QmZF zr-HKY1FL~N>o~3vmm=uoPtD7Zw5ro=%gdoee^K8luAh@%5-tO%P&?`=HIU*11%I-p zf_mO{t#TYWjYnW8RX39dM+gCe=%b`?t2r^ zgWG%eJo;U*rC5sHsj0{)q0WEht$)H|TRNjWlsmY~(TjfwNPjVR>qz1lNEx+DpXs0k z+m}#on^yRIhe;T1`TW>ZGIl{8d^L>6#kntX)gBYRW(xVA+R5rupSE`|;~55UES%Ma z9CUpieiw3&9fH_a7CBH5x23y?0pucZV;6+~=iTWqNuKtk-=G-C}I;*1Sl z?|-nk-#3a?WpE zwNB|bozsHiV^vusN6xjs%zw7J@nWQ$#(%xtu)h*=@W!0>tA3F2`hX8sTxr;KC0T(f zkvWz)4UrGV8ZpC|LtI2r$=Sni6ncv1kMGE)^4*l7GLMD3THa4=5bv zf^LLYHVq~j??gNrW(Ay`8;Y@Zm7?8jvkrt_lEfwJ?@@i9i|39~vuc|4c}v3M@+6=3 zpSnBK*gosGfzPy*8Z{ zp+gZ}^c*QsBum1U41b~+m`tiJ92-e`Ya;*k>hr(x%EHwl2~3VFRk&3zoRj~>)0Q*% zPbwMtU|J(t4`OBXp(_3i%gDu?at;>g{a<=1Hy>MJd3!l>9Ahb^17dLh`r&|8^@Cb% zjm3B3>}A2^!Ep`$n1#<<$l0lMYjHdj#}On;lEhE0=vHNMnt$a9eMx_V^qM*61-l22 zA4#)#O@3lAMwt6>Ss zQYLX7w!Gi8BwepWx;xwt@hq)rSkAtj1+*Qm#4WV<#x0%(mJbJPO+Ps9W@9-tBr|`& zuuF5M5QjPTo`1h z_I}!1_OcoNPLpMQPH6+OY8ff13K%q*S4~dMRAQ1nPHhgw>|G;AxV~}ov@dIyp2xk0 zj)(mCWp=iXx}E=453FZVXb(oG(54aa=kxB^ux#OK&3awalgC)i<8{SRIj5u6)fz~_1XMMi^A^+gIjLVBuBT+3Rv%7 z9Z=L!!q+nOaY`anm_q~cjopp_?rr2q`<0T)2 zA9`Cxj@C=QAkvOXz5JE}_SH=qK3+;#1>UBeHh&J%5c__KGJ}M1?a6%jTrPH=kg9pp z<=pvCLXQ!f8AUc~+IKVJVp_5)g}%7yR_rd(zfNs2hJwCHFFG>pPTcDHtBvB)&+odmY1tuv>v!i%kLi6q zhJSq?N}H4(+@4T;Wsc+G4^eg`uM++3ZfpSpY4iH@o4VH>+DWjmrl8L0z8XmfKz+@s zXXnc0ozKJK?c6->%%{Kr)fE4|%19Pv)JCWJHWT*7TNA(P9ALu2?s0_peGTUVKdy7V zC^$pBFxt5j6c~NRT)UPj4j*{O$y!ynK!1R*vmn&wwxiIdDzCSj*0?ZUqlbUbvX^Fi z{9F{$Qr}lF0X$j`^=#a`&qk?LXC8MJ&$o;#(Epbt@p&;MaJ}dbFc(OW4L%p}TPrc_ zoHz@@vwcz`Q7YL;fA7Ql2CJc+h2OgYA(T5aG&;!GF@H zz37cHFy%^2{w6%Z!js*rx9wxbS0NQUCFzCaVbPDM$n|s?1$olj{Rt0pG^87QfC~<- zIL^Gn{vvGuCn!Tj32DaI%ReDoydia>k;`38Eroy`&S#l(!Dd~V!|eZ-ei4t_WI6iV zX!sF(=kkbSJ?S^A`O^XQBCcRHH-C;vU{C(nNuM7sjH)I^muK=3r5^9M-+S+Sq6HY( zN?;gK$nU-;%<5HjFJ-mLamw@+e>X`${;pf>ShPbOEg)p~I!@KF#sSNlN@z!-{G9)a zJ9e6Y#1T(mU<;dcfM5+YHOoauNGhWBk~i9OdU7{j zPk5tA_6}clg)}Q>i^B2TN`KTD-1!^+hDx}Q!oKcN44KR2QMsP7KeeYlT#BW4^M}lU^(=E zE`S}?IW!*DbPWX0bO}p|$$6^i73KtN%$ck-@oRfN9@vae&Hov`QGaL)xi+W=i!|8? zFy|aZU!e-rV}#4cvEXh^94#&g*uzI$WG+I9Ox{@zw}Y?+yvCM0??3tM?VThK3=;w* z*phD?Kzc9cS#(Yii%aEnEcH5Y!4a?jg``eik*c5Zhvvqb#$VwCCMA;L626JtMKU%+ za*FHI)b1$r{UoB+T7UC@+$K@{e0g-~yVZ|l2BCdln;Dd{D!$(ElL(5Wd@|agmLp{< zr)D70WKxHHh*L8Ec_r=YH<3Yeyz7ZscE{999%E_z>#0fv;5&(K0b4!RGn5zF)c?H| zNuycD>aiK;TBv*@fHae*;QA@~-8G*XN%e8;Xh+W86eEWFV}C5&0P}wQ!p z^Jtr`ZQg^s;1YIoz|!*p)${No{bKduVK0iR~PM@wlT70^A+FtSkc6h}RcSdV0((w%4LNUN_-CJV8ngo}QoJ z^?nKdo(tZ9%YSyWuIs4n*OiyRAyj#a-%|VO_zy!^hE*O2y*3bzXX@30l2n37ilza} zC_iQcG(_@Tx#;+`*yWOQ@(Dq8Ew6_Y18d4n-iGZl*Bc%qk7VvbY#O zNQ!q-hJW2@WvR<){0Q33QN!Lms=n;JLUkV$dl+C~9lt0A4JM;(x)$h{WA4JkN23(| z?O|z+5y!c>)mGhTZwGNOMvHS@F$kwn;0WGZ1lIU(sxKYen?BL{p4OZ+ajsltHI+UQ zX3+#+n>%_q)Ksk*T3owdn@hLQKcP+LDU>b+U4JvnlBVFJy_B4^KR-)iieKW;+K06;qy`lhhYmoynWxMp!jOz4V*UZU_#N?=@~l z4&>|)s_GZ(Awv#>aaqR&sP2yKW{D4xo~k#E zX@AfoQ|;$slg@j+NU)Veeeqx>^uR%VA|dfxa6uZt=G5fa^9 zKb8x;9E#w?2+Y^0;rsJgL+ncC`NheOCx5L0ovJkrrCrFPr!FAYv3-VEAZOAp(O`Yh zc&lPSC&N2b8Oc+XjkHd^nCUl^*qjc5s%e==0gC4&a>QEvZ^B-8IKla_!B3LajqlyujDlHQhkvhO z>sUDc`=1DiZAQ2yViIQ6R83sT$2mF1r+qV$cg;^kNBnUatHWC*zYzmm_dRlR&n&p6 z2qE$!jz&5vqH(+C&nMn}`%_$T(%#(jgD$8i9AioM#h3KC;jO2DsP2pYbFSM#T-}K3 zezV?4)*bJaWiRp-#&%+d{FX59jDOBuWb3IFL2oJX$aEe{H8MlN{O4ENY zwd=-mdqT5?=7zRi^$pxk<6AbF3upQxm1Q183J& z#e0QQT+1VQ3AZ*v7_y;U>1c?Rvz{^6djRenFaFwJf5&#)mGG?2_~HOk6MsI+nYF^a z_yM=MkkYWRRd41Sk-&|g3-k7esL}Eq>fkmW8i?vG!m-W6&oo*Lb8N~fnebqQf^nKg zxpeqt6%r>I-taZPDczw;WriCM9A`!@%q@51uF7+ge_cw=IpXCiiWBBVj_d4uD=v9! z`FzH8`)ecW-0i2$K&;S;%YP`Q#a;Z(1fLs_)3CV~N6n z+nA2MiXT6!vW(|tt=tKLpvHyOd8a1_@z!&;#^Ce#V|jNu;1=;#@_(Ra&txecl;1f> zBC*LZ;=7LWmtCijds>4cjP5^pVUFF7OHE5r1iMOw^j-el?4o4Sfl_~du80dl zDVo!!mzTQzdtCePmZ!tKZ4iTBIA2^Bg~b17d)X$wJD#4e-143H3Z4IFPk5~$6B;Ml zrP#kDNpfUt{Hsb8e}BAi0mAlB0sMz3)u|u3hJwoC9L$0;N>3_t2^E7=&nuO<9HI^3 znUbY&&;8(c2H(hU**Q6}VF;2ZSHI<>58`0Tlu4I`8S)BCr*r7vX`$8q7jl13Sa@N_ zD7wSgQAU&B;G3l;dvz{7^_9s9oAJJYSL`)vB)FWj@)sO8hJU1vk92Qc)73k~z+9SC zS9DzK0eWcNt$-BnS1Lm@g2*$EvLmAh_?pLJ-gC2Lgd~=s)Pr!|#Y26O# zyKmOAOHNTYv8NA>%EzWJ2bfuBqFzD=H{+5o9Bm+{#zr7d2}Qknz?O!kR^@EMq^v3oczbxa{p zeeg@5tbP5cgF}O9OWRcEPmeE9N_zU~BRI(Z_)y;JEq@Efe9^ZzOQU6toZ9Wy!~b;g zW0V`&kZ>=PriX*;&*rO-DxfYV>8IUTH5AizO^HLLhaU8o6^YAdGO2}2r-zRZM-6C( z$kMz&rjJ+XN30>aEo0C3;ApKI(t1q%b0*`+qquI$i9$E29M#TK!DB(|my`~hD2a(d zExawB7k|1F%=7%KW{D?>hwltsHOsC|@DM1*{%_O9Tk)b852HG-o|@3dfti|yaLHS0 z$jf2d5pUqV@=fh0O4=(t7{mUJ`i2JeqvYh7{=>rUBb8ed6!H?zAEC-j=DquS_GT&X zDf4L(H^6RqNW*PVi2eF`vs?Fi_qDZYD-htl-+#D&vk%HThN}1)8E9Cji?s}%v;h;J z7I1T$*RX1pj+~uSxLWG?8!o>$US=NtaMwZ2PH*iyDGOZWKpxOiBW8iqN4PveoGwAu z5nn__<@Bx>G>@kk$&TxZezVV|4uVRC))H9)V~u@zOR>QV@?Fmnj#JaigGoo7{h1J$ zT7Sb~*KNB$N7B8sa}MDUhE;5~r6jbv->LJE|3T$vl2g#r_JBTqQ*0fl{}w;w$<6ZT z<82C7Rdt=>V^V{?g6V=;UT)Jc+(MIVf5&jYQSi=KP5p ze)}(-xk6Gxf$1_yJ;8hd<*C8!g48IOQ(O9Di&f-sVCuOVSg)q!lJR|X?lkLY~bWI%$Oo=N2JJVrHa-AHZ>C_~fE6odllUgbGHh^pL z0-xWl5SB)!FEm78g){3B+jAd*@P8;&KMB-W{;=83w69L~kH?gCBM~bzzN=noVJGg( zGZb|{Bl)E0swMdCB86(SPSaIfM6}j%mlBY!I)W@1e+)UC(Dga8-n4ZGFPJIEaNS9Y zQ{kkbM->+y5mR0o1rk%gfDnT%&s)X6feMT7x&1Ams6%wYgmJ=rs=ZPuPk&d}%IBnH zTalUBjYq<3t;`BnBrMm(LPE|-_vpHEF?I8FSvQFL})=^NYOjf zG}q^z**K<_*|s>Rd6V4tY6IptG{fvUJDX-7`;U;tb)H%p!;?p>mHi^4G!HA&iOA>= zPYvPxG-qc8&?JLTO&+~Fbbp%9iIG##sfJH13{NtwYk_MhZaBtuAD5PHY1I|_isZ9; zF4*JAb#yZ$3>$S$*8R|7CH0t&BK3xbs`RZTUr)Zvi26gg2L89^gO+a)2`HOGdR5$g ztCaN=!X0CH!i}7o(yES&VK~Yp}M`d=y4HngXGDnslzXUN!Rr1x_Bao-~E96Zl`yiqjHxI z0xhOgyrryo^=Q7bLZy!IHFB?AiJMA$WmW#YIxE-&{_7?TxJcxgB7e8mgbrKGRRH`e ziiu9ZeROH2nVpN|fP)LIyHdHkN;ca$e)qsvV^Zb~mhUQf(|^L0oF#T~LjzxEn4+8~ zv}#uGxJ#;r{-R4N!ZxkeqM87>S0~eC4>DyoMvawN(J}m0uFk<>`u;}|OcmsEn9?S6 z#Iq#fHP>^-OX{J?2C(}@7Og1j0FMwje3QOSTsQKIyvdu2`uX&a$SwVnxO2NCq+Saf zwU#ymUh47Poqw_)xA*(%Q`Vh%I@1j6w&wnnQl`+Eh?DO{7(Q$^&AQPaz7xhpaVh8c ziimp^V}B@dt!k(!hxnhKc#zwY=mbEg%Zj)}Q|;S7EP%crP739uln&HJTNl9B^tE+C zOydJjU8~?VabD~KdFO0jk?TK0zz~5Mf}SZ3QilPMOMl8ere)rKr5Jr|*+fK@m_Od} z*zEZcyhv<|S~s46ZC{!un=^K&g+Y=`&_Suci}T=h0^=~gFge7d;ZcKUtCw z0T2}2m^#6u4}-h5tP2n7acD;ojv?+c0QCKQAaSq`flRoJ$69WW@hUt64ee5+9O3z zmT&+~_lH@uR)re|=uhcc9cfSl^OEXJwZ+L~NPYDb!_$U~R~TdsdT)MG88?el6)ZAM zrDH6+xZr>7hf-m$%ws$?F=0+ zB@a)>fHCp5U#&n>3Ak?)(Acg^Zm_7)h@l;-X&xdL1CKrv)ne#g^Q)KgbW%BMVW5@S zg4BQVdIpd5K{md2xMnB$uNK=2R_eE5>gISg=`-ZeZRzFBX4jeMp06G;lvj!SmoIi? z&Cj{oxemzmud6JW-*mnB8={z@XJK@Co=Tdbm{uG+55<=lsx)#vt2Oo7%*k~nEsl4d zgu;A{GsSl1G^QcbyB3W|0%rHfyE{G5?^=IYGJvF2Fv0H;GiCSrt!%igGK*a53y=$< z+qAu%=?Wp!v0Z5-&+(ey_K{@qOuUvT7RusneJlPcmQA z3_DedWt*F`aRFxL0V($U%9M(5eX7b*o9&_x$W;nGKmF>2GWjo*%$0jrFjS`D14=@A;E>srO%6W!3p_o-1+%Uce&jBn}6s8Us z9PoM!yvC~H-B|i{(w%hjjjr_doIiiuSGm*d{J3_ENG?`J7>z&USDR<{oF%s`ay*Sz z}-@lEVF-G!T~KM zw+6a{n!P=;XR|F72R5T**qlk_?rO|{g991+?;#u9cV^kybNPBKUUNnq7Y+csx#5 z8-o*N1lsROpv!@((&c2%{Y?2!>74uWdbIp^$yWoWEY6=Tw&ms0>Sl(VWDM#`{xB?& znr;C)53Zz|P|O%qP)wl+;~eA8EV#UOtbZu78iD{U{*K(qtVQ8B0K;VuwK##(St;XC zB{A%2ALzk>L%6ms>&SnfB(d3j9&&Ei4U1yIGqh@)W8n^F$fendjxP4}y}gLL#$c@p z%1MyeM}`fZHSY%LR_Ltuai>1;dfsAz)m=W?qfm__;K-0ORFhH))i&8|=amH|5MVc%0hf*4| zvETWRIK?J{fXX$W*^SC-VE6fxcQ?CSVR!WYE`T5@G^o2xA>e64P z+b)FA8ys0=q{$#!E6v9WkcZ;Nuu{8;J$LX}+e|r^f*{Vc%LlXfcO69~)iOb>+%e#5Z5rZ^gr>qS0o|-%VQfhP|IuH4H~4jcG=+ z<%N}Ijk@LtbqkRU$iWzy5Et!#tLe1E=}8*YZ#a$7DzbF7BD>yYvvp^@)4%e9gkuzt zzxfQpi5F|2VEHkfCiQ?5b?%8@5SP&#)n>4`rKW$nFE@PnV=2 z@MwEJJ(W%Sq(pwv7}h3pvc4#7Z`AD9fAiY0jZ~e8^%9?uqhig zA)oeBM>+TYsKPvzYrvuu0-gZ`lD-Zn&*B1g3R(@eA{Zk(;HBfpQX1(VzF6oN;B32! z=v99l#yHzXddaK=_lAg1F-YQ&<=n%=F0vXbvSdEnNUaiT2(GmYRit6B)-a|A;~6NY z!+R^DdD1K))r9WXnx}u#`L2>{E3W_C_k<|=$COoayYC$6=xWac%Iq)G4UcV4?j|~C zp>`cbk=EqiaP1m$jlO@xv84|p^TgGOV-%y-_dv--tY5R4F!790J_(@{(B)S#S zvQlA@Al|cmwKD5aE6MT@?pXH_M@*&$xF0ZaX0j79)wX4osaF}ERdat&0>h0@Ef#;& z@A%i?$EqSK8_Q{6E)t=AsC-x!>>NN)Ip(>jsVPRJD2zz zv5(k=!*v7q{2B|@C>vBe=s1Bo#>Rg}sMWrrezUVNuWbVLu4@;~T^)6duY1x2P_0kO zAgfdjv7_cr9A-9T{*BUWQ~H7o5Q^t?Ltx|LyoVnjg=pu&fdvQ9fzo}Smyr`veKL|I zE&)zx{m(EP-ZQ_M#*Ivmn4K?M(vI^?$`Fc^k*ZpUQ2mjmL;mtD>6!&n{s4dKYWix$ zeT&PVS!_*2%F)q(G>`U}E-5>p!%IgByc)-J%2oHbNzG)8>o^n=Ot%iT(RJdwltRNX zUsSP=4!0YNmw(gtBLt<^puS6vwZ%i{spE_6Jgo&e@$9k@Ct9 zv7z_`4dl*`v@FkWKGVYLobIW-`XqmIt*?(8?dEY0H= zwWY+Ij#b-#l}5Gr`uZ6(`D*q$jx!~dUSFj!prs|56tqB+#zm<^b_0L<2N}IUy^!7n z8Z9p8Le!WC-mre{w3KtNx+n3r1O5eB0e_*-mw1OJ-gVW=PvY>bO5=fa6JzW8cg&`t z@|bnMLr$vqhL1T#EOKjs!iB5N*Ua*1{1pQ+W93&8`HmW;<8k*!g>P6X>g|jSWBU)~ z8>_EYKd`@X<5$i%=nj8%!=0yf=gZlKaLJmAg)6R}jDI)CJt3}kXZ3fO#^%zpt=zHo zdGgokPI@=(QC|jo-;F<9CbkBczH|Ke}NXv<_8BZsQ zC*bMh95K6g)Iv;~u1lT12@jaLXv{fCuJM5)08=`b*{t0<>0?xl&K?=*9H-Hdc*m6I zydVYZMY)}WmRq3~d&9~AIG4E2?{M-=Kh^Z|c-oN>o9t`X&_PfVG%8NVcg=h!zq6O`z@TmvMr0;jV2f(%0xr2{W-Xi!rpqWqa<#p^ z3V_;9izQpu)z}2PUDY@nR_+=0{v&r20tS}y!>$krxeu74JQ&_*`JSA}eNb=MmFz8i zzBXMM1q3=lJ}z-M1^{4F39m5Y|4)3ml%4${`-bkz{|$e~m{*~uj+MQ7#`vFK|6llp z_?UkNK~@qcp(yvexeh{wH9Qbw#jJ6N=tYcwIr{6G!79K>^{+l_;c?XvWx)Qw5lzV0_YwfpV$ z^JjWRrCzen1+w#bH?%Q_Yk0z!x!n(qNiHutZy(`Rc9-aP!#!M&i0X0-zl?{`gO)d} zw+_7$S28}g>03%`%Q8cj;4|;fuW(P13ocwXycd5_EFUN{V!CPXMfLdivzSf?_y6~5 zRKH}O>8GoHZ@hpUw(f)n@91N35?qz49_3Tciw^+{c#-zb z52ymw2H{6f;<9%sn1^=?>hqGBP<*5myGz9Ipa zhX6*P5mVd{;>-n-Biul+eqFlSQzG)0Ll71Z6`7Yj4k0^v6owLU^ebPwsq(dTNK!st z(=$4f^Hmd$EB=2SZe0eWtSK!(>kiniw#%YEHW-HZz%f1~0MZ$#8=r8VA-KzMAM<}` z2T|!lD~$Zk_Ni4mZP{q(f-8*e&-w?%_k;sJ)mg-`!jdv=*9uza69OdE)Z@g;ckYHB zTXq#Xd9JohwT`269q6cW^YR9EX@mE{Jo>u7 zvH1Q(ezwgkiV_$hR(uZqU zW62vyy`eGc7adN%ND=`}2Drn8D>NAu3nP0}RgHzQqG^X{zAhdPX-Y#vOk00-1F9Q* z%pi&PY06C}*^Xrdbl{81`Ax=(?|6jHraK}UI;pm6vhQrv!G~Q2u>?12En0jjbZ^j1 zT}1;lDHcATxfB!W>}>39&R|rgJPy%~kQFLz7|?@2SiFry{|OiD*~323_jAjh>PckY za7V$70tL>e^}h8swn?*Op_G5{Vh&2S986=&;T(5qz2d81F)Dp+vDX8YVxp@F@TMo$ z?SJM93Y4h`ZOe)W)isc+o~57~EMB|lnte{W+~gw2SS?9j$oCM1LbH)=_kN8emhYoRq zH)qf;xX4^!#3c$RK9J9E&W3c+`?_Qa>LV2ceY2>bK~--vhUb5V#O5umN7oWXWP4PW z)ax$Haxm;n{mx|_=Ks8IUUtJGfBp%Bq)ynI-h&hcM;w8EZgJ_RQ%JMp&-!K zC?(@|6{#jpVzGbmc?-g>7o@BUny6Y^Xt}J@K!};9e(X%A4ayY3`y*s2<4F?x@B?Jt zFwTRX@6apneY7=-&?y>(VvFE6vVoH5;RF&Q+}UH{oz9ic-7_ zqRIdIh9mD8E9T^cxcH&dA8VYA)ny!`Ab;B}YxhNSQR{z5gvx!D(`7&6ht>|;U(3@P zFMz-TUi9HfJHcDB75Av(XLac8+G?%jNn)1MXwUBX^1zQ};7jA)!HEB5;7iz?CvIPG z7Ze34G1Tbz@~X@C^i>#;x!-4>7WY@ub`s|>Vs(Km>YE=J-6jD1CpYQrvaJ73cUDHs zZ{d29=GJvDy`V5Zruvw6+SUtn`42^QzLK2Qv& zpd*|78?(O9uD=A*i90h7-p8LM8Gr@l*Lx$aCeT5?tG z(Y>3_T5!6Q&5&R5`-n#Q_$rQF;xi}w7c9?vWMucGOALi~t#Obg3G(4I+fs&J-oiA& zvZus-iI+3y7uNmo_x1}_ZvfnHTeyUZgR*~Qzk3HbN9j3w?`9P7b>HRMzmIBS=^bhlDFEkb_} z#$q=Bj{6*b3Q4lGkQC>x5Znva6^qcn(rYmGqGFur=;&JaUA1Lb7zHt(?FTuq$CzxE z#+P`^t^*^f!p-dQ+5!#~ed&8F2L0jtgEyZTaQmH|T3(&k4cuiAN%TZhVfWX_?+|yV z0l$(DF2!tuJLpxb28TWQq(bDIBhP=zDD77RP~8c~yVtwD3Y<5LXzxh5&+YKSu0y2v z>M?36Rbb<4iueFP4qHFV*Sz}bf!QrOzj};D38PR{;nIz%7U%XfW};_a8li(!5S~*G z&j+hS#3Zg{f4DRn$Zk#H*ljf{wkE2;^_*aun`0K-(^IGr$v)5)X!vs(9PEDzQ!k<6 zcMv3ez6G{FEQw;gok9I44PXM67bHSSf#L+Cf$r)X2|-I4ZYrfVxk;btQ#X=})!Khm#f%4LcJoIj*U!Q61v*22spjjw} znduNSyvH3R!Q`cj(O4P3wP^O4gi_x* zaGAWiCuWPmapJ=;fU}Qj*lmN5-%GmQT_@$N%+yl4KXb=>j1}^w8LMRNVLbH@uF|HZ zkWvHn=AZH_CHz`CO{Ra+{Ip(0wA$C^>=&VeXC@c!)gLj!kEN$+brAK*?v&f+OV=K5Ce)#W6WZ(3#Ip+k}Jb2w{AQq53xv-K2``^0$0JCgh zjxoK{a^0#<9##NgpU!rOGN^r+es-hJh&~Y(dP=2h+|Qqo`SD z;Si4QD~Y(R#&(I8(^Y`nCB*JQG%?)$I>8-W+uSaC(sGQiVZbC1RKkhi>=pJh{`sU# zL_G(`@*4DOg0z2s3%qrHk@6g8ZW&NLD<&}5a_n3b#O(b;nHN}3P-@EIE}Jo0iPZV@ zbyNLo^O4oO&fH3Qwnk{rC^AC%G^XfqycXz(%3#(FMv^rZPr??>mBP&cIY{^#P+MjpK^a{R+u_#v)eYwWp&1QZ?;@2WcNr7vpc+ajpQjPakU*yuim7Cxr)rAlF+OVa zuI{n3)^Mt*c5IE;DhPcy*O!A5w4Ja8MFYQ709rl$)P-du_*!2D<{;B;Ux`~cB>O~s zK)mZ<8ghS^^~MiMo~dRUTL^MQmx@ntkXKywoMG^i&7N`DfKgd!c5BcTe zf;c})c$aYuNdp=&& zz2AQa1E)d@M-qD@)TFr`{dgRF-M=lmG7SvO*KTI1mf&e;>1GPz+vMcF3EVMY?=%Vp z)%lG#(#^i(5>y2Vn$1YbuJ6b$L!5EOGPiBrEnc2Wy5w_(F^#pn)3{!B$mlhm{$y*_ ztQO{X<5iIjFCaH}`A`lhXw=GJ6*SWQC4qm-*9PL0j7852K*N=MA`f_(Y3q@`^8x-E z{+?x?Bok3K{I0hxu5Ts)uWXeghV?PV=A*7&seBf9vM(Qw9Qmj35bBF@Nr?X%M;iAO zW^Y^6S+#QDWDFaOM_@-xBPLI%(=-uMR@vD&10EhmW}S;sJ%!=XZay ztm43*c2;4KL9Lpk=X7gh`IEWGu0wbc@HwIR>v&g}e#7$^fQHU9Zou+?RsOc@DE|q= z5PId*zXk^_0Z%?!Iy#hdMQa_Z75pI|^qqE3_C?^OpyBPWc?*N(&wFuws=S0$QJ_^~ksLH5knq!B$Y4167kmM?L>t?{w94O?5tkJHNjc7&`xWxI z&jAn0cC#yUkKdL6M6$U^Z!Lu83J>SU(0GZGm>$(oxmDNBdSmhNP8F2M0-Bjm9g%_U zfvdi!;#$!$GHD_lhE4Cotw4W==kEen!e`InrLjejl&^4tIrY3CoKP&;UVtEvyeL{W z*aWp!oiEO0Wzzxtemj}tJR*WTtS!(eSa6qp;LthhFJt9Lj;?zHJa6I6S?#tJ0lOTc zAQV7=`3kQX$`@mN8oau$e+l2vL(3|L=(juu5kdSsZ>X$9`gp(G6o7x`&FHbBW%9<< z%(HiPAJwQP`ln%6p2R$DZT)e_)I>`LC8 zOep$uDuvbOD95sJ{tHJ zLuxDgaH+Rg@>Ap7`FnrOi#mNA`?T65QZj}%0hV8g77Mmvj--E1rrzOki47%}?JVsw z^4)ph_u;Ba4M2J0iGe@48b1Nm!$j>5L5>pI9*FXFwF7G5p!e(t_Z$xoGI^)D$REdT zIb#m}@VU$JG1%(f*Sk^vQQb(Ob`<*mBEz${ADCEJddQsHe}OI$)FL9J&>3!t&f1LR zt@3m+vn9(>_lAGl889-ARz;`$-!J}B-a5(*ds~aO@r}9y*-sqbpOsrV(zGMk`7%giiDIjnjKHEph^>Fde#lweG2}yr7>1xqUernI zNstDm%Lw9Wbg6GUW8{Gh!7GqO`&R$wwg=AYBH>;9+ZfNht}HG5wpDY9K`Ew^8Y?HO zH(3{HdzmliBe;!@ZOzVtB&8lY#Z=+bkEFw*THoMPYW$R4j~B|cucnK^PqO+u-Z^k# z?`)%Oz)F9=g759Hi_Xoo-reE@F+JMZmerSobaK~yYm2|N1w ztB-7toJ4?tOf-iQBCr&EtJc`Q+)2yrb}@TVOC2~#{^&kS!$!2R>AkxM_^45DmmKK& zt764dCNY^v(2YEiaJ;ubHu{IS7lxeLs2km-rqe88B+V1)na$tja#c^&7X_cgl1aXa zX&8S4X%NM|NfTQiM%&hE2npq9MygQ^j#j0r`vD}GON=Y`=J!{3Ytzv5V zuw^A=-?~Au%xbkV3;E+W$uPqZ+f6H|#%F&;VQ|Nn`_GBkRCNH-H}x%VE)vDAWzN$M zljhDxF=m-)0Jl51*=U`?7XC%#f78Gdiia+2T1|9x{?0CPu>|)ZJpNc%Rj@h{)LnPX z)4L})KT>st+8U*@Y~42St>gUWf!xG8cXA7A;Jim4n%853NN<5-}Qfqu$QR) z&Tnk$=-3Dg?}0KenrMS=J>>1J3aM&C5ewHVZFdSNB2Q?y?^OonGt8gDqvA_QYdWtm z-xW(hv-(P$Wo0$#sVO;dq|pcQg{qbi9cAp2G40rO4P2f-^$l7O^|uMEZL$^61EYoW z?`fuOCE! znNuT%Qgp{CwUnB2c*zTjrtCWCNR70*5)@J%*K+FTj|lY*&M2PSDu_1#!Q?%VKflrw z34gIebhaXv-Ns0dS^!AaW@;}}c~|1)74X5^Rv|qTNQ2<08~T50Xq2<}`@NDEr@wqaB|duW7r&Ui^P@_$;`zy-ql4Cq zj<{GvTJMr1_1z_qFBJNY|?`UO~8H^*_mk)yjK7F%;ND31^fMlb2IDqIwB;0=(C`<5v9M7u~AsuBp z>_a-TCL#3*JpCUy2(?05Ow>wChU+Tg-~LOwfyB)`8z_mXgmpe^(C)9 zFsS}V`9Y7Mjb)sN2J|1lmcUuJ7qUeClBmPRfz8k$sEzOmy~`KzHe)WD^-e{A%NLJq zHZdMU%JgaJV|vm`J4oN!x}+8D$WzqqhlCf+SU*yR2ataRNK)^Qd#LKMhwRecUS)A1 z!vIYF*A)7TcR>(gOAd-K6>hp_hXtE-Rw|$;$#PPlm_|cu-+G6KPjZ<9N5G2}ZKDps zm}aS(7xBvpFKTm)1dH6-Dy$bmu786keIc2WgEtF$TN5B>b_h}7Mg-gvB<^N&avz3b zGf=Ilo!9OxH@BG%>%UvI<|D)+$yLQP_ zPgOU-(@07$buMkbO{I_>>8 zid|yK-ZgF6sy;+=>6i`VU(?F;i zSxA2?rFAF&gwITu8HFmC9$-TR|BbB0Cz@Elm;c&E4JfB{V~1YIcywRBy*f}%i_B;P zoX?Kkap1ps${iB@Sr=mpqQ-xE&SPqfERE9m?}p|V4~VpWfh>IHHe_x@ z30(*3_BNYG=G*%YO^b(&R?{!?8JZB?eR3Bwm`O?gxAk{GIbNp4we0i!_%7SPt`VFh zBsJ-?+5_3Z6D5$}aA+WetT8KRLASt6V~x@@W)h-9mG+$n3(Hd=q-3vj-EBqVu@Zmj zM`l8(+T2KY_krWbbYF;pY!p5%3~FY9w`S_0T)N&9^VQ8h;qP_cw_k@JHE;@L^VrFU0$YDJfz1&KDOq`dy)O;I*Y5h{a@vbUE+d885~OZW zE8Y8!hw;li^q)>WKMz<$%1cXAuKHy8AEwNbGj8aOhk|_;BAiMcMf2wsR@X}{ey>AS zswA%aI!b%?>T6i3`v1sr|4N;h{ppUbcI{h-L%(F|hp~~(@|I(JKEA4MG;M!3n<18A z{wiJ$zPiUUP#SSV6S{>&8i+qMlnqel6d``rCaNLrqoJ2U?X+-Q|z58rFP`4 zM%-xdxn*>vG=c^HIU$7`&$+6xSgmF(dzyRrbrj&;QBiz?3A0#$>eo${qsv6ypJ#oa zbX?96*@nRS>%%^j(jtr3c_icF)%#Vb_`RHR1)>|K3B8!tzIi&X^SzT8@^0HeF6>1 zI`=(HDQ9jDdK`BOyYYR|(3x{w9w?k?;6d*i5t}rrTb*;Q>+fi}FhY=ef|aV+NLUD`i%Q~Gly&hDK-RMEHX3^!u&xaivo zzS}y|dOx%JSeqB^&V$&O6_bHD?krQX=<6!u#zDA_2X-c zsxp5>=v5Y-j>++#ncLgB#?1E5Ni{_oxCtb1O5E>LR3JdQ%4)UQJJGz2iToZ^6$HLH zoI_u5VE6Wj$`d3a1K4yz4EkrbUkYaAq4{_@`IlN2x6_KMoU}g1Som0-1^1m_EPQ0} zUvg1-+6CY9Yeat|Xj~mV_C{z~lEnRwbMsNJ*?i1Wa6V4mvpIHS99#F^C8N+g7bv*y zUDf|?3CLhnJY2+FuQ%B8xhm3tu-B~?6C0s4+Pvlkgzx&?)oYx)b^jLN&$&;>^+*eV zNo{c3Io@RK@GSaF@8dN^Tg-E{0Ek*}l{*|6Q{zpbYutYbiC8PqXMYldZ?C0C=CD7-aIoNvSFS16LSsThz-oR0oKEoHJj_*w)Jk*_q~?CuP@8XTxT zPU`+_avWp)Hq8zWEYB;(ZCG485zIfLkyui#o*eW{wJ17_Vyuc9)YblJZo-fP?PrSs z2_~f^&8vSmyjW*0H0^_VE-}db`#(%?K+_1XtLaHbiu}`Lg(Si!@Qfs1^vpl40Wa^{ zRnZZN7x0hM8yJ74H=Kz|6#iiiwB$EI06%;-*nb)@A*6m0lJJOIHSHhPAY+9<1VX+y zK>C{k2%!(13Rm0E>kf8upZd!;%N?vFV9aCqI52v+a+?D%Fx2{YCk1q=sjFq)bJN zhsG|l@G5>iT9gua!)4svvLev;Y591*$vi3Mnh4gtKs!zz@1`7BR6Fjiv5;H~0|G+u zV$gpw{++Ij%MEqMUh8|nAH?uuSzcVaw+;4!2g-5Ux~US|vGHU34;dl|+G zB$1!*7s_E2*Dl%&PYX1-vgFna8XpzkR~dgFe#iSAZ@*CgNI2%LgCfo`5Ki(hOE5>U zUD&L0R+#zta1gf3daUz--kGnMI+_`W9T`V-T3QY+?7~g|P!ddo2zztL0ZAOGY98$C>*o@HPwEPI#J~x8^*mhD73D zoOgT>;c%e_lpB0|dkc@-(glA7i^K}aiu|RF#p&gT+x0X{?H-Vw?oLT12y;E@MN{4& zr0>8e)VXF+&WsgHBqPMmXP^nKHE`JqE28O=~CVFklthQh-T-&PamS6Z9KWXPJ& z$rDap4oyB}jfeHG5UmW)aU;~y>^CH_|90IMZ$Y?`;%0yM18(;o_0oT9Q-e{xMJ^D~ zro(eE8twRBVLMXu;3{oQBrvhepf=dHq|#HtQ~!!qi|_ry!}D*%I6NyxF??sHr|~@W z_IH`u?@A6>FDbsT_)OSMf^DR^d6Am3I=S3wNxZ@O=&oUy`b!fkHK$wq%BSO2CS+e# zNJ!l6I8g*kU{8|tf|!3G=ki^;`=4)25wBI)b)_9}Qqt^{pPQ z&n!cv7IUpeZanQ0r`Qe)x$p-9gCjza>AmgA+q?x-l=+5;N{$~LuG<>*dMi%XBC%<> zFDBt^n~Of0^r+A5|L_kD71&FgjFls03Von~V1XV)R3$GB7)12li|^Dm=#Mk>NN04} z8Dy)l)g1rcRk42=xbUVNR?Zk09yJpkDf%6Dv#)BlqRY;B^y^>3!%>E?wsruUn^h@d zgJd*OnDt6?N_~Y+3(m0|B_Zer8q0rg04NgDxtqml19KR1j-JSgwEj3MY?_qMvbi!9 zj0(h#L2mFguwAgp=>9Sz8xe|5p0s^(O)QA`{`r_$-r;}kCON`4--wQO>i0UJSrO9^ zb#dw&KX1@AKTK7dBxMgBSLq^lyFX5{%>v6VBYn>U4`1WmgC_3fv z$8V~d-Y-eNmK1VE!i}4j)Ct{A2Q7&chZs&V(Xf9iq`1p&0VeUd2mTY-$>1S5^{**4 zI!(Ajw@qGiWu~gO7ApdW>AYVK24Dz^mQYfP3laJgKo;NmEmj9`SKm@z$dEWwW4vdH zr;f`BlOdl+aN-UEybFv8`!58F4if&_N^GZ-+TMl~QzVpViI?;*2gHWJ_ASCu_x2W) zm6d_BU>6E*sLz^Wjxf(0o0oqp6YA4LGmPl?Z|SR%x38v*C=9wUua3HAE3PDQd}SIQns)N)OiaK1#e2|B^4`k1tXmOX^x?om6glN!=4ewI=N_6C2a zY&KqU2lk3bz|LvxOkI7*vR|c%$1RT=3-{I3HzyK$Ug4WCoD?%Qk#1?zJzNh-ZM_9? zy(CG`8h!Y`f~;CP$SZdAQK9&NHmRE|pSjLGg3OJEr@Fej(9lp*jDq?cNP|!$1yOT` zPkwkk9}rCAU<9PIR?rJ$4{~e=D6oGRHT=CwRCD_1F3p$67bn>_RlwfapazfDauX-9 zn77Tjo4&5%1z%GYca^=vCy$1P*`<8sC{>g#Z@PrChLSUR5=A&M2Ah()4dGrS+X2WEk3VRnE=J}rW&4q-rIk!);`&* zMm9Ww-w3X5&xd~9<3L@HMi7uMt+RQ;A7frLi{1=*g!XBA9M11zGx2_)@Z(hMp!-x@ zR@!ro`m6gIbvMWKU>3(>*3alZo74$GBW%z6BYm^Np+Rk@fk(Q3+{lFy2y`o$BwqYR zK1pOYk(jmbvZ8ZpKgZ%abfa$rpI~Z8lTbSb14-sWj(7HmNAN?-Ds747(XB z9kuA{nRKGZ^?@C~(>Z@+Sbq4J!<9Y`R(YiVOJ-+bVJo*bhqEu9xA!5R>RoPb7IU0p zO1sa~fRbmy27#ey@=2ok3SM83mO*!=pot^7ulQS}p8;A3GjDP&WM2x8eEB`GzfGJ6 z3v0}LK4cuc(ppvUsP+oB@q8323>AVBGcdQUrGTzS`wa1(8IpgpdSm#rkr9lgb#+~B zEEL+N^Ravf3x??wbhPDIXP1^u*Sq)D;9o(y$7{tmYWgG3<(+d}J^}lbGY0eRF%ysx z@*iDdcI--nPfgqRJ*2m(a;p2q(*d@o}7#Rrl|NB-Vw7}O~0P75qqJMaN4_*;-< zgvBBd5cx&3IR`G4l@VFQwoO!7=zze`fh|@^s*T2Jhj)L{Qjk_@zB(U%ii(OwS+UGq zpVxV8qOQp5AZnaiLx^_gj$hSk{Ry3dB|kYkWE*_e$KPDwGk_tNtpS~LuJy@(lLpFf z>RzEo>R`v!O4d}G);r!VQEL9FA`>K5VcGH1!^A*TmWiI;9334q;0*V=PmE=>uhYav zIH`+TT|a-Kqwe>av?6iLU5IX@GRy7XZb z?iY-2$H+A>pPD8iwi`zRNduIEn{UuOhSsJYtq zA#SF*{f6S!sq4bXUsKyP**tqr^Ya-p`?AB~dU*NOMAkd@#bopM$DlLU%>l4?c zp7BD!-X_`Y`9v?GUv%QG;Wx7u|zGqK5O9A5(A)tBM?(7jL+8HMSnhtJP zX6ftKvc6zA<;_Q;wqI`*-2%q?$ct9?TYu)hx+Nv2VuXfE6hd7&nn+WIx;0aN-e7;C z-Fqm!#dg#gh5ZYsQ7Z*Wp0o6A#w+mlhv~f|!cVn#kX6ZjkZcv`zhxlHVtlE=gWh(t zYzXa2pvcbOz-Q$M#5P7=Gu*uFd$*O(U~h87T^vq1`265gE`ch14IqJ~rLPL^QQ-0Q z{CrX7;&b?ki}7Yw7GFW3<#dhj?hA5XWfoAB+q7;U-Do8VC7qfC0B!|m(!dpF;wNeCk^08YDx42HcOYc?9hf1W1W{y6W!`Hv|N(IR;wVItV*^bg&Rt^ed z!YCSd*d0OU@eU@^gPj8DZd>@WrO3^w!s9D3{k2N}XtdWiZqw?yeJq<4mcFw9t^0^B zmI|?ej8_Stqo9sKpuj9+ry74Sf|+fIh|-{qh4P`@wUDW@(*@O9)ouZ(!%#wSBt6_Z zesC@|^Tb>2{PB~8&u8#VX?BnF>u(vw3NQR>n-@F@@Ku@1i!Knly!Sn8uYjjBDfl4w zCLi`xjx#ZNJEpxB7;X6kUw_`csy3sg!}FqoW@i1MS5=@H-f=F(kHvpl(l||GTx8f0 zy6OOp?aKjzozDl_1hUF{9&Qm^2*AB9ttS^IK}jy_h;tsS6sXgN?Le7dOjdLjLQ+XsVZ@r zeIe8f>yB%M)ygP-tn+_4gK$XYKcpX8+H5J+%LBQwrU$|)8){XTsc;hJbP=8|494g^ zPp~@59fiWKCNjpl{q?OaUmHjZAa|(Gqdq0QoK?AW5x!mHg$?Bd9rfh)t^ia(tH0Z) zNVStE4pn3zl;WdjGBoyPnUJ9Gn9EF>saS;@yo>xEIS5<*blT?ZxQ~5*(!a?<827ECX-(siy&^LwV{vG3sSv7#XEruTU-8Vw+AJ5dZhs9R-Lu=;wm58Dd5aq z>M0LBaiIyHZ3}uSAxJ(?U#mGyd}NDWUFsIX`{*x!Q9@86bdObXJ==I!Z+)Wqup2d2 z**3Vn{hVd~?&+$>A^FySx}4$Hx|74%9PU#D*RPBysmy12Z|1$f9^=|QW#w^+Sn?I{ zfFBy5dG~9Xo!@)Og_d{CtE&laZGWYsz4{BFtHcxGTOt=jM1?TQk3qZ{d zPCkW?C9iQ0T4ug9Tdcf!cjgH_nwbk>+LB_=`+ISDRj)AA~LS1hMxw~$^iTjF)R8! z9eU-Ie_opDKwp=kFz)eex}Y|c%ib479lya($2fWi`|aL110m*!W6`4ZT*oiPRbjqQ zAYj*9r42z*umx*-Q9t4-NGb_Z@|V%m2V5~E=C&i_FE;6a5x_Eg*&%ww%GF;`NqM}I zT^;RqU}rIM)>1dExcalR?)|^S?uQQOL0R+9jlDpe*~A2ij>vXv4yF!(k$;oMd)h#6}#_sZ>@dK=L+unwG(%eBnbs2U|yaeL;hb`mGx=%|0Cl06T+Nu~f z&sEpohEAU|7(@6O_0v6fCP<+Cr7jp4VW5_&emc>&*bi11xEmYjSzffv89UHSQ(dzL zh6FWXxX8(gf2GvWw66&$S(BzumGjbG&Q^q+9S z;G$lCR?xdLA~(#aXvu9=@jaszTV({Fv$M0Oel6%?<$cAx=-Ldv&2wP%!WDZNbL_=$ z1y>HvxLV~Ba}IZ{G(-!IkwYCNr-l%wzuY_lQE@uuTM)+gYx2 zry4(o{&qu*wgya6sos_+^wD~M_jNu*=kH=Md56Ncixls5%kgW-rgF)sM#5l=ryw`R z{F8Xn-DIu4MGR;KNKH`(iq4X zyIA>_I*v+2@cx=~=BMKyj@mgfnVV{&;`E@S`mw*&gv>yxHOaRJ7EX)TJ;d@dex0?S8m*BA~%wY ziQSRMdo*^$zZ4a3(Hp-Ux+Nq{uSG}(AE}%=ZIXuQtrb!=#8Hg%?YK0E3LP)6XHY0I%N9 zM9x=E_=9cp5N~trI`aRc^^O}+z5NM9dEUKw@1?em{*MXA1^oZ7z2~<{^QqQq$gaSO z0pWXf+&7kI&!*H30)U9fZ_=+_70}x57mi<2tQ3~-`H1X(aX_qpP@er43a>#U1kN9z zTie|A(M?CJBH;9H{a1e?vRTcw(>6mb(FB`Ztpd=hxIBRXbUU3k&I0>Y8u9yiBk}=K#-)#PIvt|~|bvr+o zU`t8`!EE!p0w+;_jfTD+74cahH`* zNNeBQgEd1bwTh~x_CF|~Yr+x#F<|$R(&N-1Lp^E7hD_phQL9dXjN@>TB z@a1*oM^3CYI)IBG5x3e{^$y=-^hoG`mZc=1U18{R0Y z04Kkdkh8WZKgx-El-q?+@BO((Ee)yF>a@<%32yy=tKi*VI0Ov#UBF`2z+(Mq5v5mB znQa;JY!ovgkfU&QVBT~5oduW5pT)|19052x;sxW-z8ow7hiVA)w4Y_{{r;>i36We8 z;rH}fPrMKQ@=tsxIMJ#bv~jgS;lGT!AtqWmAfn?0nh&AmHq(x+ED?cze&sl`I3aoQ z_2Qv_-fH5yN*8%ho|<+vzM5iZe>{9(ymhbY!clDWt6lRW%smc$|5Zt>TK$oV4PT9Q|0?js&Cr5(n?a*OU*R0?;&(5%aS~qFYnw(MK`UgUE8l4K(zipPWIbPsL zRXoI8SNjTsc>SAq0Z%-*Zr9p)>9saIH+*1VFa>iRUmP5Y_{40PAYrh`ZQbs>T;ub&a}JG_3}!k`-qLO0W??=}9tt*s!7HiX(!_lz1C9`n?N( z*52;3ei7MDM6C0K zqf7WTik}PGJ>t~jrL@1d8Vd)Rh(>LHO7(;a7bcu|CqLhf`f393r=}DA3_q*e)>mf(v! z-=qFkIZL;&Yt6I#Tiqmn$sQ*WaqV5-+gcF32*rcFvyLk_!9cq&?{o0RqG3Ue!fPZ2 z67=+`7ugZZcGeJS#D%>UbsWckb6V7(SAzFJetmo7teO>dc*F1w0hgemzuL3jDH6uI z8XOhX61vj&fH~Y!&~Y5%7Znw~-C35?>$#AhnN3TEtVquFv41zbM)()|wi^@xiiUt) zfyi^)F{G=f@23;FI51AQ)@4wZ&!<)6k7$wA2eVpW3Cb$O7ywtrSJ;bxQdG`f3=jxvOS>6x zr~Iu`_b1pz`mRKybBzd}3r|ENu7%fI_byv_M{YbkykS$+wbgMV{<2we8qnn%R2X}| zj*9yJEV?vplcRm{PPV@l4GpDpzbl|AACiHN9@slh0sqWQvWV zzBfWY_*LTp$qGkyl<++_;hv3BD`yetK4~xfS^lYX zCPle8f*Yva=(FTVc}u|4qN@b{LwYHJbRx~xNh`AMEDZ1T+NZ*|-C0B4(Pmzb`qZy~ z(Zzx?%NfY*`jiQO-by5dLhSe=T))@;QZ(;qeY^UeNkPH8^OLKsrALp#JBVR{_b_@`*EjLRE z6psv@c=ek%FCak%3U)2t- zIAwvOw3DXTw+!!z=@5rxqjv1>*;A6gX(Jk+RR zHRwaSOt=4k><|YNshkLi)^Tv*7%FV@FxDB{4~}9dbcT(4`#91Y9R~ zRbU?9*?#@VjXG<)f8ST5Ug?|!4|H#_AMH0aH;IH~!zCekXsA{D#EIB{Dv3X{&GgAj2mmb`V|yAsnzNFn zYKN${`|s!QSGeujgUSn5DD=&@e(7HfTlA2f{X9JgJ3m_K#;JvNK1wbRE0K1 zj~gX_6l>tTyWMF^3E6+-DCA#5vrpJgCZ5Vs?_};p+WCqW;vX2z8T9Y%pOs zX_G!c3syqI^5zNYKJ%H;8#q*}s#=T{Go931=H^@n*BHvaic!DIl8JvP_yQ#lqmmAb zWS)rb3BaabY@WgXDO&Yq0#SKg?di}n={bylB+}a+iPyL%EiW3b-5tjWhN0Ei4B;hz z75by3OZaxNMD*DCfYv6Ltb1Wp=pPZqzF@_YaLLk8y|U&>nD4|JOhUclGLEBwn98-; zv^D1byNu%XhU9+h&v=i|lH}}k81?5AhdTUIyGl|cg+s{)=qbw}n~YAp{uL6f!nxdk z911$HIeFr6@GAz`jtXfGC9eagQSbV6@!S)dX2S+rm0Iu{ly3QeTp;*NNeM1q2ExEx z9N#d2Wl5Z=H}2Do5*=X&`R2Qd^06!hMz3GV>TeJ}kfWhv8Xo#yrMt)Cb!7$-la5+p z?B1V#%2vxHy%BLO75l1O{o9b};aCNKC~+_vJwTZs=5_C06{ILo&{s+OU@jlH`)arn zkrGZ4DUOgkihl2XQ)%iqf@TPg;X_`^^dx$L40xAAexWi>^9RMt4fs;CBO<-+QQmJ) z80CmD-?LzKCLJD1cP6Q+VtKpuiYmT($a>USeWxn$oOU~#+|^cu_Tq0#Z3i%a;*VBV za-^s8)$UZhzOhnuQePGeDus4401#*|l{T-7FujBJ;^kvKK5q^IK%c`<#Q?`7`Cx-N5BxskK>nbnk& z`Dv^Rf&H=EVHfcNc*ni@PNEP{C>~7@QK}F}Z_B5|XQn|Huid$1FEK@YEp+yMe#m%-lHosE@Gxyd1j|W1Ub~yxmyP-lL!%vRX!8wQHiZ>*QaTC}HFeFN zMx=8VjJ#1zDClG%j1ot zO5Wr1DmkW-OfC%-d4jO0OrdizJUp#B`dmBYY_Hm5S%u}?bMwhYHGNLwet39R4y^)& z$ZvQG4hRfsQ4DwvDsrP>`Q&GZ_5b%-5P(Wuo(*#>(%(C7D4NH1p`LMHo(W_fY<$=hoR0r27N|t~L*D-OXhAlj9 zi>0>bwhhOAyn3YNkdU$iGdX#gs>;vTbmMOKlw{2nPEa0i-_+bPIyU2|K}5*`B|ue8=JmsFq*^1Mhuzty7iMhUV`hQS z^w!aR8cu(;u1diecXQ*KO64dspExfutHYK1r;p0nqCgiu@WQDh$^OF)p~69a@lJzh)xB=-S#;=G+iJO5CGc-0&Soxt4Y)p~4D3}5^{DX;5v0>& zzCObKq5%GXm+cV@RZ^fzjS$<3HBx8)1PfywkJ?xkM=Ji)!9c*v@UxM^?x$k}JcTf1*#uM}6M0#DX_`@gdFE~WTY&81s9S8?` z>GX@QgxnX6Ure^Vi=Dld(j3XPw(U6(DC6S zL-+9)=D*-)zJ5f~YmjJv^hXYBcsS3TT)zg@N@JEH>b~YeSN8v}zA-dGwt&;~ctU;F zH|_0z;_eXN*b)BWbjhF#i<-#rR4Cj(d+Jp7B%}RP_=F+L>ed-cVG-s{qDHvDtpFr? zrqKPv&B9Y;9mpqXl6+P1ub-GOt&BP>jR4$%fal@F>S-W2gdTgQdaV7(2%q?X*B zOry6gJf$7f{<6I0UzNQTUpqsWw64;{yfpb~y?TZCtrE@eS-LbtJ$BCToOUa43fo;Z zmD!IUCutqKWdh9N4y5dITAdANamNa;q84vLhUy(HYxA9#`_H6G%J+vRVSAnMwDcN( ztrg91oyLA#fH%tp(g<9&+*n5Ne%hD3cWyHC&>EPwxgZH3c>k1ap3yS9TS3{7YB={e z!=&aRKDQautX)8SWS2VY=Wd-*NV(a#lV`p|)!qMoocYG|!6N1a2d5lF(xf_;ysdd{ z2jG;me)zvgq(%yHeCu>ock4Fiy43Z5Z15Wb%F|OE+vpNB4tFB;%=*6G>k&PXgUN>o>7uqw8bwu8%Mi2~bJZw@OTG5y0mJpt7jZ(Y1 z7?HGkYUV7u!m^3=8H&byOO?)t^kjEHKVPGK4*O>cSaH{j@lE?*928;$U&6%xLDFk_ zBp0g4Y4>%UD?Y+^xq%!FvYi*GtI)evkP)mdJG=dy1IRR1%T8O5cuCWU zSG|0#Mi5Ue$Dy@~a%P$D_iVeZTx)IkaIG}%buBcbCic#xialJ1DD+i-$PBU3maV90 zR+k45{D-dN?GAMC8$L=?9z@PuJ6B0L@;$v+h(~L_v#m@Yncs%Nq5aBrF*ehvbIgZ1 zw!*4S#oP7-@Xe$pa$=HRNja>6wKpDSGf%Xz)BS;!NQyPcnb2cyQ!#MJxwh9#6DR?; zpWW>*9UtOmk?l#WO6E3y<6g{RX5r9niSuGl^zwsAF+!P{9jdu&NHnRNncJSHoa2wy zP#c=x6pjx0zodlsCmjU;Qo-Tkw1Uqd!jD4x&7mcj9!AQAoAc}>C4ROYf}l)81`CtS z=0)et93xedkhz(J=#y-*^G>vc!2PAN_MRkb*kZ?-jlx|ndPl>5&rZbFsZ0CpM3P^z z0;3#5$gv%s_^q7@c9jEyxI18LI@E1SsAsH6B{Xcflkegt7vVtLoEt{m@G|{|QcyPV zARKVIUpg{xvmdT3$CV|~Pz8P*S?|AJH}rfsBKi4i4q?@^5sBKr`wUj7$qGU>L%!=| zd+2BM)3v;8LyPHu5NP5mctZNEO$D7fd%Gv$swGp$>1PgEsET?7yk|WZMM7`sWxwHi0DN{Y^2ZG7|K{?=kx0A1th;XXIsbLk)x&cyrq|Os3;ipx! z@R%S$W<;riw62dkTlL%A<2mp01s#ZOx1?I{7Gh{WHbcCB-4B~9zo<)|Z-ZLw=db6z zuy}ZE5n!bvcAiK)+glB^36_iz$^(XOln~wU3*aL#+xSu>9q|;}Vt$Os@h(=H-j!?d zY=SkCQ4MN07vS1dESV}_bcoC+s^fB3=553>{MP!xj>Eah@_s$m*?q(d$@~zE`OWdI z?#JqlI5zix)X0gp7H<0XH)@+>YbLPGL`yB)%R8T!*ij4( z7l#~bpidN^kqGU_)`FGGPfog-zb0>K`2=ifF(q&wYlZqep#?I3G^{$P+MP&l%K(*@ z7#*4^Jq88+8=f@A{dvPDVVPG=eL*ybF`PxE)PQY&713-Cal7f(bfUe0wmts9*VPBp znUq%SJUQfSQtWY!p(5D|_G!E6{ui zykE{FCVrNeWQ5xrlrrz$3d7qQybxFWAiKaDTqm5u$Ty;%hTt%Cy+`()lJqlL>%(xm z^eKCbrNypz`QiH{uRlnnckmSUtv6LKT#l`OOS_=3uIvAO3*f-p#heQzZHcVal(JpB z5w_*+*mRNYuU{B}H%IzsCukvhE9i%OKXh&Qy(($jFC{h3ov;{OusYw-AkJ7H5AmjO zlE?H#3%$EJ3gMxGlsHh;@2s|>Eo=*%Q1D2^QXyxQJf_~xp;*i13xE7*rxPQ@yi~J) z94G(%!2_X>tP#IOyRHqh+gK2m!~Ma|H=*gPKyi+ZZ!V;qj`IQ_N`x8Y9I~}${&40r z6&-z+I$A|S%+Z@fZSWh_ea zt}`M{vRa4-Wf1uC|A*uBlSH?5^_eSwbi})*r}$Lu0Ku8$_$@D_vqMnDE{!^ZBz^I2 zJ^AQ98#d3iN(a+O+7(eL2W9Q+XKECy2b>|Un~oFGV@eNs6;sH(3Ljd?nV~(+vo9#A z3=7fEmJAY^9!5=>>aN&)ePWz~>5ov+gVj_H5vH|vtep1a&+3@wgF+JEtz5@{TDcaT zM4jYpH4F7pmnyZEYxvXR1(%VnFK38EOac)(oA{xGR=tF+BNfI3ueMsRyX;cL%I; zZq7oIe!X?S4_(F~Zq?ivKD*ltf(Daa6xf_@Gt2F;iG5wqTdFUm11JmduAc2~RUz($ zPgRj}WksFg4rjRGDGG9ZNkq{Ckv7HF-WREQI3MGO<;e+kCV@6T$5knR-Zg3_x=Z$D zo~POk^@5eRas<-o9d_*jt!&a!)cfdb{LV95$J5tpn|O{Hn=$EnPW6^LdVQ6kO%9g2 zq@;o~DH!%#?8fleHk=!M(TPPjl~GbBgS7;RR34&xv9WT! z*S43)3O}a!c}OFFroZ|d47k0rgb}8eZgM%HFzj~jIN+RSh04QWA>JdzUa`Ye)=`Hp zU1`v{Z_94ZqFHuzv!>E)m5per2_Bf3A(G5k(xRIPLnz@^(zH)Wiv}YHg6&CT^f+`P zaYxm~5kUD^)>%SYCi?V=l*{UOv#UaH1KVbd%(d85Z&Z(e7t~DLiYu1FGuD0dw*C^o zhHi0RCb^eRy@{_ntKb0;ju0hz-0&wC4kLfGv={S^*BZC=46zSvv+3A;KIEbm56dy$ z&nW7TL!IV`N-alG_gvaKhMx@kNE!NC>kdB6=4U!R_ac_+oYsNPq^{I5c?~_@xiXc6 z?V3Lf9NDpd7?`J)YY-i2g9Q_b4gw1@Y7b55;uO=*7B3#HFiH;OYuohwjegf0TA3vM z$6!*29qS?mMsf(NFsN*2(B^?YLMSBsg>>zf2`ETN;-aFE6ck?p(!3ar@-KC7v;10` zK|S^FAy&g%1SFp=k$cH9h%a?4v>W7lP8B;^~^E&1LL3qE( zgUE9UzBIv*2x_r7AZk}xVMj?Fk?5Q9u$1NW5LFGAjO}^L>t_!D z_L?h#8FaKAa!;yJ2T7#anw`{Zo{^5FSt&Pt(LY0(Uz1PzYCJ@i)8UoPU>(zx}L{vq`D938E zO_Owlk2izeo%4nRg43x{mr?G3)Mw-S(u~=dD_3}W{sd%_9+Ev&m&y`A#qe8+$r?|8 zq!3Q2RHdg@lln)O2Bqr?mxnN1xs7JS7NKCiNUid|%pK0B6YX;V<8TaC@0{)ZMcvc= zl1T999!0B;op4t9y?4fjgFFMQ<)92#FE7JHi-m%xYK`y?;J<2kHu%HkCQKL@7^gU9 z28QnIqovO(DnTF9&970)F~5;e*`mLH)bIG)6gka{-xDiX%WFNE%zHyl);v1(ujP79 zwho*(r`TC<%3MAO>z~7L6ja<58M&#N@HgVvFeX%RhO0T%k*wG>$t4~)*`b^{musWe z=}24;DxU4zWB&rQ$A8K7RR+M_U$!OCR#722d?F%gp5QTRv!csSJ1loRIlpv&l2&6N zKz#~SE2x+bPh&kNUw-cn%{in-4DeEJmAA6p#neeJ-f#_g7lR*EcCIq4R!~AFxX0(- zSQAH|)e=$v!QiZPVV)e&tOU3yl|-{{ZvZNj^g)`bRY6{0JPjhU(jLQV5NJ#tfAmY+ z3u_VNcDBTo*E)sJ{qFv+!k=@0yRGgr&2DD08N$MvMw9Tf*<){Gn4F#>{xCd2lW z_a7Rn3h1106BD4zogt5Zu>jH9lT%aKQ@_!OiI4V13^5w1pdA|znrKRJSbfg!sQr56 z3511br}4$JH?%T;mUIoA^R=m)&EVETH1Wc)6$VO3Y--t%|H$Epz*KPo0E})`fy!x{ z`%n)n7cLWtG%^4U$EGncK?s`#{`@DwPd?~r>hLNqz4;IVn)8W&DXl%Sqh9L01e1v$ z$@e36kK64h5*3iFyTd(Du0Dk2<;a|zWM|G=DbCQq;%o8OrL~-aj666?RTb!`VTc1p zOLo)_@V+zvGjIhhel|bWL1?c;2}RLKV}@xZsTm?3H!~d&)|6jY6|nW-CqYZEb)nNT zGxr4}6V^h{kN{wRsfqZw^G?sNulrKon|zJY5`aY|DShOHUMEM;cKkzHIra>{O4h#f zi;P6t+S#Gt=O-#ED(W8`^k2M}&y?VHb#+ymP0{=i^dc}bGt->kkqM}4Rzm0> zopbyLrtKJdI*B+znQ)20NNsmCy$<=2U>nT}(ctnKV&M^LbB2QmmpM6Ee&cdNW zzUoVbpOSAN@|w_t)LGXT8yUK4*7w>Y)SK0ylBO#Smw#XncmA-?JIKTPum5wGjv0C1 z!HWMrpb-)wSzZzkDIYd|IMfSR-h1Z-B0x+0|EPP*u)3CIZ8X6xxCVFE1a}Ya4s+t} z?h@R83GM`Ux8UyX?o1%S#9hCvbH2UyI`^La+~4=-c*c0DtE;O=cU8SrCCtR}QALR( z`Mm*I@SMJLS&P-@e>%abA=a-?GVNJe{+s9knZ5m?G{X}u2IWE%!A+Pne$X=vRrgg5 z+fMf+lB$ceaEd;2xBG`{jdPgs;<)&)Fr-a?<~TdeTyC6oa#_>#xeMaoIy@*W7}ld1 z*N}U>E7+f?^~M*?iP|+0|EVxCl7E<&8A|8=@5%iljeG4gOKmHMGPvYtq2op%eX=Bw z-ijh_uI$z^;gQ9|LPmXwmWydTKX)1CW& zlFvc{cd10ml9bcivsb(`#{u!CuM1T9&jH(tD>dxP zk}onBuiqP&w~C+~eppPOyrR*JEbsQw!(~wrGn%MCN65&*At8#S6*x5z93@Vpb?*Ar zY(o{G9$}spgG^{>gN4rnQHdduF%!FgIUC4%=nAtFzU2Pe|9T^kd%;fB)1$IrPPn4< zFb`Fi*!;PNFmGm1<54Cv58*)?&-+uc{q{HLRT%;`(-~aLrR}}gxDTZnnk!f->O$vR z2mzg*sYuGcrC#4^y3~nv2(7++&&7m|#q6VS(THL%Zcf}78qSO{wydzv zJAE$c@3r8_i$PcYqu0P1R~;B6M!Fe6N(hg!1H+k~dU>q|q)$C*Dg&-vXZggvsZfH( z49`f2Y|^K1!r`J9HzjwP7d-@j#)-$LZ>)W1q8F`G_Cnsy)ULZ=H`lzrxGnwZ+Ummb zyp=khdLUVL`d7@&XW#C%F_e||MqvFPGn8Jw2b-tl490rmZNJf%kEK;+)-|SX4N4h)Nr0g zr3K(vuWVmbctL14X_Qr$c0dWG0k1I#HB$fu9NLA@Pu2 zid2&6G2bwOl?kw6&^o_dZ4OZ~`VA*~qz1kkRap|YqO{=okT0yzRy6x~gK`R$@jkVVruZYZ}%4*uLvBb1IPdhTw=%@gbsTs*Kl9-mRQ3+Fh7 zvg<#;b&3=DFlUR4hrA;rcZn6WgCY=AyAha6Khb-&!CttSGcvfYg*~8gDz{hqP#1sr z&&YB&h+OEO5znCRo^`U+*Fb#+Md=#N^$l`}qC6Exg78Uya3X??jqjFn@{$&&rm#|8 zX1Il&5<&|}DosRbVxsRiMJ1EpdoGX(Gxx1kcX1(kbE~z=tK+_AM7|3Q#Zlh`*I}~0 zZm7}vYSuutMc1{neK6mk6;Y{rgmMv6mi;Os_U?QkgnWfu(I-jl8$B_^xM*qus_UOR z)*ieS?<&B5yg4txf*4r>1SSHONJRA9R>!trmyo3-rULS4RF9oY7tUsNCr7t6eEn|J+WlN$C4q-0Ub8%ZleGK1LTnJgpr*!T2h6@Je^bRg z6>mz+HMv1uUs`LWnDtHicTMAoq;9P}EzDQ#puLmW{IZ7QD~S@MC+VMMBBL~*izE5d-lq-e**5no?WP8Q zV)!?;2dP35b@P~=#1uyX_;5H0jtI8t=Iz_v|p@rr3_zl9j9FUPol za}Z7%OL@<~|iG;lQunqBRW#foRmY#G*=T^}(29lrZQ5%+4rafQ9# zqifQ%R5jsZ^W#cz#C!*A?Ue8~^WK+%AC3Ed+{LQcFPOpGYRdD*WybuZ;0YO9V9|8m zBy)JDQ;T!ne#LApXpw;TYQMLiV(({v>BX3pzm;UXsr3 zs?h`0DPH@dI|{hnd8o~O2aM4EvgwqZl9XP3o%6yjXt0s<7WPG5OHS`oc;rx=UsrIq zePnQ#QPijp{YA6i#1eqd@~u-U^=8)T+lfAWQWrC^(&8V>Ayi#|GUCMEhtuSL(9a-C z>&}nA?%-1*j;66-QtRXxzQM-1ka_Hyo@w&+t8;tPHVc*9JN0bEGa&Ko0hf7P*AD(X zBO+xh^)xwYOpH08*onCv3XknymkN6d)SP${Y#Hul?O81HS)QHoOBHi!87#ZADyU!% zT=ylLFo&?XdQ&uP2A$AAm~tV1qqzv~pi-xja(GNsyh=u+`7G|}Ju3d2g`ryJW=+S@ zL26<~3EwqiT{}H0E#G-6>~J2rL~f^)ir(r+4nb!36yl_b8!Ot*W8<@tzOeHHpIB&M zRU@3>$2TU%Rm_}srWdpv1`PyyrF^Mh^6=mWBbCxs_fAULA!Gx;a1;oCC30uWkCyM3 z5fbqXxAU3YP!2m+q#vGJPV2Ej&~8!Mp^cJ~6FgO)DGL`aE?L8|)=7^W$fEs-}tEizA&mo)d`La_An#!&~~aqD_C>I==p7ZsxNJ_&fA1i-+dOx zLnffRcwO>hIFz?LFH=;1He|PQgNu$pUwR|+X#WBNE-zYA0J2yS>rE$miHS}F8qZ(Q zoaz@M#}OD(AIW((Z{uBU2H!iP%!c%gG`ve0U<5Bw%bGi*A6LD4UfpdDIBxu&;G0J? zzc*T4&b@o}NL?1r(oPEan_v)&KF*LIHw-3K^$rh53 zppsv4miIgyss>kP9h}%p5n2ids$8xaWDU5MVyw->R?0$3fQ<IsL1F<-z{}zyJ62nfV zkzO?1;zBrDz?8|K6c`0zGJ_R_wrA^D`l7rOfqW_sD21b_qh9LhBS%{|qL83&9$1;> ztI&KS_gzn-2bMMnE3vL~^D7%NMAT#3tVP}?13HB)R^mFvjm;tpGkGQ8`t7^(5yq_vH&;O0A{VD@d#O8s<@&0qI734!P6)T8==WojBIn%`UvjW>Ge?QS0{_MlC@i zR)ovz5|I0`$d4aIjqto%#_gttZsWxPEb7x`xby$s+0`&BT2-6$MBt0OKCz{{MgUc_BNw0aIqa}V&tl#~0F-#ZRQPWpFR z=7}V`lqHuxM_S(1H_r`3^_&FW!^mA_btA4{lY!Jb@20QNuP@_wYKTSSIdOJpp^{Y^ z2XC9dESwcGH)?xKvXQ0quKKje9w?`B@c#RMuC99vb#vskWEA!d7zxrr)?XX^QitdL zp_hk(tR=9I_p=WLk*_Hm5{PW~AuKqNuSdCf{uGqNcVJKgzO*^Jqo(S!tpF!PZON6k ztn!$^Wh>x)2o&!4c=NqlT6SGUg(oscNoO4A$V$yPJl%TvVTff}k@Ss`(LjZ2SAsi# zbW<6bvF^@{29V7*?|ZCE!yGlys2N8qQ)i} zt8*q6OIoLp+T=VdX_Uw2R#D!;f((lWdEP$;X~oC0b%o#VEi=)=<2VFopm%wJp~o&y zmsuUH^4)1;Zje9fE0+J?X*EAr(776aWx#XCEuP|Qk*KR*=1CQyCkJChwj!!)>@z+p zjGzw@0;|Q^dK5@(=X7Z5LZS!-pue2KFJg+7zj5H{?3?V|7ULKkNbpm=ZZQ-YE~0R?7Ie~@UrH3w7~A&8)#_cY4l(bnH2OH)Zx}iF=x%_2f_^l& zhmn@&bdTP+KICX|@=$@Ct!bDfW&|QlI_IE66M82E$!)F@T!|ZlL+w-xUe_JBV+BeX zS&iIi0q#=APvaafCI|VeOAYjM8i;!|e6M$^&Ob(Ny?xLRd{Y8)QK*Ss6Y9Z9d;h{(>moo9s@V4TT~!MdC%@?7 z7P~1@EEJe)9L&30NO}VOs6mOJfSKY+)&X@*KZ3o+^dP z<0okoJ0*UD_WgwAb-yS}we2|z*O`$0+gtQqNR*?gnodnwma#at_lR}oq3;kspuycc z`z*4JNTxemja@n^BBAYa;FO(9*|2xyd%a>cpHq{`uJx|D+-0HijcfjLX;Q{qX_4wl zJx(MJL-)}81CZh9m8FP(-#1zX_pu$#QNAptW?=XSC{{1eQm-ldvrJ~?s%|h_2?}>s zJ>8ILZYLT`?~$(La-4t$L|eVJ}5eIVy?pq zQ$R9hd%({d$!GN2i#o&mkn>%w?%Ybn;R!{lhxU^?N+jLCU{*-;K|5g z)23Db)Xrq9x+|uC=QogJ9=_}HcB!UosPEyGf9K)A?z~m67iXOo=FMhJOU31NF4s@6 z7}3-23(_OBysx2PFmQH?go$!?7bsI+j8axB|AkvyPwI%sZ7VjEO13*_D0^tW)KAzC z{93`O9lE4FsYNgOj!fI=eVqLivP3+{JFGzFxyE=Q``D&`M2FBuKcV#KQrHML_TP>> zdo0uo;p$vYqnFeS)IDEBcvkome*+_l?X%pGKc_2n$9J{=3v&-BlIUh$M-kSurYmeM zw8NW4Hz>7aE?Je=V!G{3T~^U-q(kgD@06M>KOLW2FO_hsY>xR}Su8h}TWBy8z5k|3Q5Aw0q7z-x0P2M}k=>81iS-=4L zdH#ye&&DH{{a2IsMgI8RLk@Md@acB(^wP7&iw__f?3lInpd-0(S?_Vw-j3=w2Desv zm%(-ha3_93nCl#^dD0jbFjc9|CHtHYtF5Vq+hovx+$W)jyk+b7js7ET<&@#`L4H_7 zq-f^L_4VQsO)h~*E6+{rX)v}xvJX&C?;i1-ir0I-dVPKfStQf;i$Ef%a@6L*7k09! zU==I6YPOTWSNG1HyX4FN22MAJf%1GSg>8DbfR#7nVa~Ur1qRCduw!isPg-Hz8ZPXg zn<+(Knd%sD9dtd5we5#nn{nRq5nCCm6VhNm6r^21aR+lx5+7cI^g zp!!=Lu@yV&dp5_Wu*U*+La|_|8{iSANd5(Yn7_Nl^;nHdOnq=yB;tLyxRzXPZKV;& za+)T{MPrF@IeBt2eH*Z3te_4OxmcFrG}_&N+8k^`dv9WHo@df}zwz9q#Rbtvr6Fc= zHfcmuf2@8X1bf{(AXV9v2!dOrU%)^+{awcs%4!PPrxT}SzSmZL^HVMFmt8S$pNRzb z!j$VyQ_oPj#lg27i~aVmq5^OiKt@uh)Qg8_cknL(lh_m>91n6vgy%6q9v5&m)JPFn{WP8{B9c;ZeLR-5 ziH;IeDk>}Dm}Q>E97lQN$;zP<-?Ddqm%{gZ`m94%m)~k$=qD${pR@VVJo=*H!Xxid zrK~p!eAT4!j`9f93>OZ-Wv5Ka6?NS;IzBCMyNhG04hUG#I!X>)gn+9V+O9AnxH!2p zm%8fH7H$JNMZGJ5<{&0%bGj}j1a+$a1aP*eMBUB?YKYL+ApIv?Cr3^p{5?i`9hW}4E@y5RSdi?;Y*|Txk|kmQYIn6^nGH(G$rhZ9}>{Ef1H1k zJu4%wnVQG`QCVJdF~M0wPxEQSri1^$rR7-|v?Gg|c_(U4KLxtc42RLR*5#Mu%1(;8 zd2QkH%tI$7)E1?$Y>h=BnjJ)dgqR-P@^#|)DTXBS_d2k`?}I=)a|(jN%HNZcl5+jt z-wu{oe*r ze!Ct8Toe|=dg1pe54-owo6+hl`U5X^we{*2t)J5Uy`F<0vC>j+C+MP*zIwvWBh34& zB=m$yCzO69#oHG_L){&6}(x=pp_MhcdAs*sZd`~xhVZ*>^6l@c#V2%t7f7aW9HN|(YphykL zBH4efo1!QQ7Z>j4<|dcZfo$|4Hvyhl^5b@!cQLN1lFx?wvlx=XjoX#oDD@#Dd6s11 zr@{J9@d|3Qhvb|&l0S4M9IT)H<;gE+H)fa)O{6&#HacLeu6J2~`axih&3#G(s^ROi z9S_X!K=6ukbm}=ke~?SwUzez^HR%&|(pej%MP?82)%6q#oO`C%faW+aY;y6Av#%xC zWvzU>KQ#*m1S}+W;g9Eh%w0M`Lq~UljPWSSxJ*ZJ{Md7G-B_qepIDHql`7?P z(`nd(t}i!Hpi1v}iYs@lPD!*G<4ecQZW#$Q31^vko-ZIpl81-f+FmrQEPz=2H5#A5 zl6nKDDiRLXe}-IElWJ!hsg_S){Kb;Ary4GNjLrb6p{%%K|CW&?^XQHCS=TD)UU4!V zee4Gdx0>?VxD<5F(^zn;#h8gqni}vjU=c~))N`?OpQ=h4(1QJg{Br41UB$;ku}pyN zEu8jSsajGZwQuB%K$${vskuvLv{#nzNfq(3%7v`ppp8ad#_H^)s4&yXV+GdOiONXuEB4H8h@o|A)T| zsjFjee|nxUQczOnYE%P;hljcD9m7uzlX09?K+sutj$?75%$v^yRvNb0pd}$0&$E5~ zz~LSc86o#*fpn7+#1A>Dc&FRZ4V?qzrv5Uqn#;S!VQP++38`eIj~gRx93Gnc`Ez7m zEBkd46Yvc$1?y!|@#hw$3F$hT`))Xn358^3e{oD8(PxvjfZ-W6Wp%p~-2tkumgEeZ zE4|7V|MccC1{=5IbqAt+t}(&r@{SG$ryo3QwTx(`RVr(d)|J{L0?hTnDqVWn{pZmF zXF&hfEUS{1%<)_?CiY)xh|5p@<&I^pm5i`B=YY@&FK>srFkd*e*PaD}>C}MRya#35 ze}&e-Z!Luht`)DfcceNw4%{j1noj|M{qt2PKF#M2pSMyM8yWs`Oy(M4QB!X110<~4JjO%XTe|<9fkf)^t%b=V)R0p|OJ}Kl#17`|^Fo1>$ z>kFEL{8FXEBuFxP%C#7!r_pD(mcp-#Pc{IqnNc~Uk+>t&WN2zN|A{)f=RM}PwV(#9 zG@^D^{pader(8WXbOOIG0NfO^z%d<7@EmNl0LSp>VqBntAMqM!VhDI%wZh!ffAFsJ zV$s8TWA632jY4oul+5k-b7}&>da8PAB6j<3o<+`q#p^)CSnI-k={d?1qIn3k*mnk% z?0dn=fw?eGtH!W>riyK!x2(-|&3KAh7+b5F45O_ld%kwLybXn0mu#@g*>;+&Y^s%g z*FBf;gF+#L|7_GkrvFK&-3#8-f4g}oTLO_D7WDq-2x&~qnlJzUn3T6-JEz-M_EXA!iwol=?K4e{>N+a3B(n zWUZA2UtJ=AYq|RU+;0>Wq>)Zp`E*v;PJA(XxCRF2E*xHNk%`X*aNlFyHXR8lVK%AW zV#+WMy`9Z9ac29O$BAn?=u8RS;BzknBp)QmMJ5qOEuZqxFaBLduZjSI*Y#BBFYE_= zY`n|70Q2E#!%$nNDF+Fgf6%?F=_!{1nz7zkPcy08?_x68oEozw0Rxt7qBTUXgLlLn z?EAj*1Q^RD4qqO^$k^tdep(cAZQ~(~a)F+k%5Ie@-B^%aTU<)pQ@*V6lm&8~*)tR8 z-tQT*dM%UUi`1s&1TS5$5#KG0*!-h0t>{z?8m(zEGh(Dz2@l&&e-5*0^*%APmL#K% zk4kx?NO(Q~{ioN@F4; zP`k;Uv$x6ujvt;j*>$pY(6j1ZSjC{?WR$Dn#QTR`0%x;idY5<0jUHXMjF@%t^S!PR zVI~v3W~Wab1^Huxe-)g?3yA~312hbqYql3JcUCGLoe0J%DPB!+8^-N*TC@J+m#Pt1KJf)^rlUfS!gOizA%JQrHkg*a3vrcyF3LJk3?3QjcLKx$13v zFGdTc&gqn+8cuE&1y1Qj?Z0z(ffNxuTSm%;S+crUU9{NZ2DBIyD*{~!cY`w;Lf6CK zRqQ)xK=Pnde_@BXK#48V?^Uf(@PTJol*$zH*6-NwfLe&_dkVvc+pHST0o#HUlsZiu zthet#%FT%-3hw@7gGtp-gwi#0%g_q^0&t`P0@L6}bro;DFUM$6$8=U(DV>qA>fSL# z8&u@xeV>ihCAU{_&JrY}KR$XnqLnl}-qz4-vcR51e`*`7^J7f&wnE=A{ZGf4c~KdEXYI|8n#^Lw@2@5K3Q|LL-v| z(y*a^QIB9_a=9Q`*VGF-$Fc4e5KS*itf&@zXgrBB;1tjGI;VIT33R{H0d8NL;H4XK z$ehso;yN8d-Jo!i3wB`=_8Zb`TrJ_rppw770)C$lc!{~|wquReRrP_x>m;=y*ZLA0 zf0f&Cs=}>Q+9duR82U91l{lbbYs+-jel1_p0nl!!(fpOEQTr8IoG7_kyO0((hIp(e z`33ukSHxJ_feEzTfyh3fQKr_})J~%p9NW~tdl!V<9m~xoiW_!x?6d5tOO3uJ^^&d@ zV6J3s0t)M`m{4@Db|YtwbQI}`vdQ85f1F?^Cz3YIR?P?q7}#jlR}B&;njGdQzq;D{ zX1jszTX@2v29W18mH%lh42p@^rN2G!aRsT|qBpy2AJ zXj#HQc=!{Gs$4)pSzT&A%$@>U*EHc~h}&UBe;eYLiy?=RI=58Vm5oXQZ+xo$_80{x z!;2RuH%*4F_4MW37yqAy1xUy|WaP zXON4VV`4sYtW-DUN0ZCKyT{Kw&NKj@+l6aS1;fYr z^C}|YQiQ7s@E#?gB!WgR4ZIt-B{31|ORDrx%QQWVHDdIh%eOHEH8Fu~ERg~BO9p+Y z^pbdw#f&DX7l167uw5CwC303qVP6x*4!*?IpvIuHe#>#4q?oxUe~zPdMs*#?^U};@ zF-E^nd3=0~qlK(S&$&tM+}2oVkr#`dHIFFxq>wF8x3(LEZb)!}r7SYNkdJtez0HWW zzo{bFXjS&NYZVdA7y8gaD|syQxuWj8m}X${16IP_-f^0dmFl!E8Ms(^Qk|O}oz$mA zq!&=7+r=SgS2AA~e-?lpKG8s95l;&rHzrwJYnEyVjfm3DoURa9R`;#iWeMe&zE~iS z_i5mX)NBf1oB|`F0{c7hW0$849f!s$o2Z5EV^o{9w~Nh{6pcZJ%NmMTSjl zO^GJ?K~Fs7e^JfY&ZM?MH1R9E1**n%l;Sq_t|xZXH~RI~Xskedc#hfKqEGJXL^fBd;Y4ZDNKs!2|oXW_1IHn|q& zUdZl8*C!AeB1dq7~rXX?nw&R^4XlJUxb8Zt!Ji+dVrQ+LO7e6naqq%NyuGVtsgIWLS(HT!S; zJ6>#(e+L*Xbt_%kj=6GF`X~2pH?cn`Ydg*H@vYMKq~M}kTsons4{j%v7zSnqd@s&K zCbYrL&5P9q4FkL93zGUh-=SGX*X0>qbn8t&$&beGUi%=MH_tAFPUjAO{>V%r$wdTZOc-EjikBcsJn6m)0pY;nUiZ2q=ZoaD$L@2 z?w@%c;{94!%*m<80oB|sB~`>yz~~h(e@4Ku#X_zPeeuO2y^HU>JPi4MC7@cr{`LpC zSEW|$F%&k+Ey;Y=W;8eA-7+)mlcd!%(lC?h*>k{rX6=GwEKgz)p1DCPA|`JPR{udCKh$Lp!1lQhy|s^T0G|tWXxdVI)kHRWn@J z?R}JpR3}*BQX<14#FTuzQvcSZ+%Tm08@ef4!aRuhjA)IJ_s}jWf1n-3H{n?S*#wUo z>@Xi-=Sg9>C`58F(D;R&9Rc*5>UPf7)U*OoGK3SYX%z*xN1Y#l{f zNrS)FBQNT&%GBaOcDvvlIUzN<{CeA>gxtvYEnV;|=XZhI?`ldhH!}jNjm)Jo_ zW#z^D!D{P`TAgcuZ(V}{2h*A=5Sup}hL^Xuf-5*nz*JqCM*{>cvzi6JvQWAUmk9OeVFzxO){^U-v*x z@iS&r%sWclifHcl=^uvo1phI6J!q$uChsUX7s!d}f9UI6>zL1RUxMzJ#(R$KBMC~c z?CCjm%8wbgxW}O(w5_mZt<=4XM-uN{;6?K^Ae!W82E2&Dc1QnFK$FU5Ay74-} zxLTG^f7`b%U&HDcGZ z`#UntLy0CQ8KkkaHBZZw1%bu7k&e|?Pg@b=K4wa(1dPq}BvTzpVXag53Foeh6)K=HwN{t;jBbOMMli7Xea%?zc@yiJ zT|5d}gWd7&kW7#ok-Kh@pPC*vDMWPn<=VAYB0&TGBiNWbPZJ*xBtw@BhwKMpoL^xW zf7dEZUZowF=EVM@$=bZAtNGa$UFDQf=YFiNVV(zw?fh-{Hw$ZHWI-FwIl~4iFh~ig zH9lA^c)obWNw^t2vF617-W_I+_ED@_f0{^q6(4fx6k?j55_J%A%vi3KZy8jcvWCsD zxEVt^#8a|iotY_3o)LPs85C8~*DIbZe>AAIiId5ZzFnFbBlR$vBB4E!kyd^98n#y^ z;E^^wUhZ!vlAfv>`Tj!OSgoBs`3!yCge^+EAnw&Y=o&rG<^3#E#o+Q4ZVP%9pp<@r zi5d!+O={IBS3rb9#u-mMfe|H-7 zID-8|V^(;YU)XtCXVT-A4oab%5OR)Y>2qh$Nlns8_S9xF_j3#PXk20bv@|v=X7^|q z_cUt$ZRz6?WwJy0VzHIM`ld{P`U$s6+F;}MVRdbVh*^gj#Ec#Tc4KEuJf0m_1$I^ah;os>&zh0ntoR9n;9^AoOMRFNT)&b6- zVL@;7ArxZlG(*ZwVB&c}L0OiTA@E0_i`Ikg$Wq>K+6RyogVGqDIFl+I-5nwaCW`m1JNVdVL9`@c~Lsxe_yo#OkQ5T zAnSGY86dTe z!xkYDPl|^gO;x(|{wi$5qDk8HOaMuq%hDP4oy*6p&{*Bp?}ZwW zAI0CzUIQt7X6I;8ZkC!6Tes@aXfeon;fhj;)^3t&e2vt;5~v&trW%#70kkM&SYeJf zF$IGrA9*ncJdZ4543)eY6MQabVK%}@{s=zjWHH5hBkIU03H6)2fBa%Lq-njD4$M}n zC2&}SCYpALGE%3xw-c8Nc*Xm++5B+Pfv3$q4gfoB^{-{gvh)QiaOv8G!V-Nx+XV~3 zon>>u9K?1Ge19Z13}{qmrRChKC1t(iDP!X*6Mc#yj9K_%Vl-$1d$Fr%Q%K3-!%5sU z)UFNVx@FX75`BWAe@XM6_+<>zXrDh&H!=4!;0>(Ubc_FDQjU$u$}geA!8b4O2>$Mk8eyJ}w01-z@hSFFS zWTl7ly0Vg=50%WWXkJZ(rU%EC9X65^iP@FcYS_rfm@tk#e?sSg^~~-OlA}-T!f(+` zGD00!MaKdZ*s?z-#p2a*KjD92{0Xr;v|Z?sb-A2Xno7FpUfpGKd5=c9b3S-KZTRfk z28rQlIyU%nUHd9&0juSk&hU&tFkK~`!RHApKlLV^T-0ecRXGLb@0UM)$s&gx+DrX` zveR_M{h*Fnf1FWq8VExC`Kqi2$bJr3bELW(axwU=No|vN(&@MG0zPWi1QoAv_G zu9FJJF0W^;^Dr|TgI=j{DfEV~8=H7#IK1l$_(j|JsmXRDLL@dSelj3g48r~07c$O$$sSV4g#H6?~)NfIs`3T zA5r_K6+yQL2l5{)7| zq=<-HRn0p+y$UQ|CuHrGSsFz#o)GyJkpPBV20uc)mP)fFqhWsj8V3QTu+2lG0YJi6m z_m07%C#Z$TKoP)*uT#d1f5xLKTk*3&ZVRMjd2dI<8@{Gr6*GW!!ZvXw z$Dv;_wk*d@xMwxQQG^U?3MrkfSUP^PN~!c{vkIlN@Jm42_qAQMm`b@?NWKC*Y#dRn zK$1aJS~eN2ncYi$00+f9smu zE4N6hZ8lCYo$Md%?=)r0=T=Y~UKTN|-TB?qViU{v&f`p-mseHA7ZLxRznKZqTWQfwUX`xJ$Pw-_>72K>nS^DoRNK^ zkg@W~7oW^;tU_EXu~ZJMS7c+8rlo=uaxCRfaz=Zq}7CbO8@>8mit|(7l z27?g6PPtlQUwD5>(U54?@!K5%$_5SjdciIu0>l~s#!xbHE>uRJtNv5He=#2L+wAhS zqIYRjPyl^LApSsPgc9?AtpuHLMnb5M+^cnV6Oa?spx5-?YIdxYdXd_UE=I7j_NAVf z1{BStQ86j79#Mc6yT^IwGj%Hb*_Xt`VaZO7Z1T06TdNs#quOz-dAIguf3sv}clYwqEaUo_$dr@jgrf&DR6g6`NwhSlB zGmyrieR6$BvDK$@`It%td8ee7t8!)lxrvP1%HrM9Gn97an?Rt#{24BW z@{8iy`&h9Jr)*vN7GRzs?UxP&D>?N}(hpna>s}LzpPhgw>69_e52pVtkZ<1uUt*&R z@Yq>I%x1!{=KDCh6>T<*c}(4H5_|i_5bi%AfvBYYfnL^mpN0$*{@29W5AgE}{-f1T zc&t$Wsbn4or4UF1f5V0g`#<%x|LF^%>bo!%M*6?M_J>NR`AFB5Zr)eB{GZ@Rw-`UK z%6%n}g#S|kOEGxG#|1I`m;tK)0Glew`H%$s|B%FJHPxy;1IIrnrw5_7U>4`X0Qo!A zPkbP)uJxnkqpF(P!c}dix~l50n`(5wC01RSV(Pzj2TMrEe-An_K>U9WpsJxEN}DyA z&5!8k=jZO_m4lH)e8<@P^h&R2Q`IzdvPz2OcI%1zgdJmOhvho{$Z1-&V)qFr$YARA zqZ0wN+ibZTXKrB1WNQk5PWCWUmV=Xyj3Vy)|Y9Z&8Z z!AeX^*Lbgw4|R{sp-BfI7+p+%7MLxf4J&BO^zfxn7jcT8DIEY^le z^CBDDX%)0hXAxHMrk(mV3C=CA5Q4tbcM<+T zwTxZ~e{x0v7_g95+-U#uZ<(3=QL~d#TjJz@bh1ci{Ft#@-q4_0Zsh35_QzoM4-13y z%95kP;IP|_ijPNc1A+rHG16GZ{Ib^r5}wy}tGaRCS3(iHVP# zd2UWu>SG{e86%w!N6U}5mOIyvR;8`>#t&;&y&>P+{mAC!?>KIg+uD!*t~fe<^H*ut zo7E`FfAqyM6&3%Fv9Pdu%)j!$G!qXs%hwUN@rGIHaE=eADeUEM zfAC*k;iHgfir-u(0PUcb?^mY!1hkOx{FC9XI7RK6^h$c~Zl17g&qhW3{tf$<`72|6 zETiS$>G<96Q=3}dBTAHhFPxoBD-{ErO%4k7OuvZj2?(k}dCivJk&(IN?YZGXdErqR zzJ2rmWvA;(esb1Uzjx)gks!x0GAet2f0A~VW+~8jZb{M29tK0jYi^D7Ctl%Owu0x}!)BixK!2XCx4G6hHe0?r#Bvs=?B3W&1hL&WB7L7=OS1!>f6ee@%RT+~ji0z`(%H zQGRSl&2GLT5|C?alc;2&aOuQhl(x9r_*jvr+aT+wYf7gBSX2e3} zMHbIM3yTlkLM@h-mP!;Fdw`ucC42g?L;XEbX5w#=1YBVJ9In3jQj8YnpHWspCenE5r38Gc(Fz zZ!lPa^>g@N9Q-7MkTY?e$7#%O=|`62m4UUx0N8R>o_4srA1(%3EdUrgE$dKsE^sMqPnSFN_RngX5!e-P+4?W|;{G~xKqhy64kdvX_kO@)*(R*tWji0kF5y%1Osn5gpx zE!CwDLYB#*6B<~cZao*1g^S0|NI*46_*ZFHYDL{p{ynWQAxv~!K6jQNod3Hdl-t)2 zwQe5dP2Pauh)HVBb0yf%v1@_4D?m|rU2e`ZF4Hy7mo2pr z3jgaAVEs~pjxSK*p8*~kJ-B(QDXEk9hG*}QCyEPq3Nbb!!y1oGk`cPSrZ`MFmJ>rj z|L5+5677B*E4Q!m#JFfd`N|2(iFrUIhD17m970~Hf5q8WxF7Es=FJ|Oz}WL&gm^ib za;j0VPW(Pb-I|Rgt*yqIJM03h}>?!Q6MTd1N(=zVXXhw;s4>|9iuBzqOH+R zI@+;q+eyc^I<}MU*w&71r#tFc9ox2T+qS;seD95M?>lF_pL>stT~!Nn&b3xmg5}En zQ$^jae|~F`akZwMW-NT2{L!acm%XEwH@hXR&UqDe*=>g_>hDA((i}_pC%WekEBXI6 z;f7?#016 zl^%tF_YuA+DUbeXgY$3Aaf*J%QatU)@70KPf1KZkQ+uoeEguR&ZQg$3sg6+z{^f3i zuQw{14dO3d3`$M1>?zxGzI60i*`zyUL6T4Ua7n_h^wj@y2`c%9OHFs{aJagmn#FVB|G&FJ1C1<5_zziIM2 ze`UtXNnEEsAX*yCkqeLATQ#sA*bG0cM;(-t*oNv}{a&}nuHByd1)+4+w;0~TM)nXF zNj~6XGpLR?Ya@L^)QCoDgE#`4*pKK zu0eQyFArdjA-uyE<=*t2HQeuhMl7ECe;T|sNFV#i0jmQl1kR7^`RipY!f3_Qh*3$T z-i@o=Hazs#K8)wDnmQwMo1qpZqke>YoKWNw;{Obx!XTgZxv2trpq4{2`>g9_xt`$? z-FuISv+{)z)+;$v;-$}eiL`}*%Ph>h!0yemh7RLghSP-$-B)LIttW|(WOhWle{lO6 z>RubD+T!sD@p6;ddSFV}ABjsd&QR24gHhIt@m#zUlGP?cXbO5`!fmQN6Z1b4O&}5s z){W&!d6>g;&zJpf^tuA>ul4-#{kAju;Vx2(M(TOr*ZvoXB1WRv$4*c!Ea+2q+|z}S z`TFNy@s{vsjp>gA>K+K}oB5DGf43Vc7=(pP9NZ~BlB*aBN{*pbkfJTXUH5p6F1q+p z)ScP69OHeh3WvYav;wkVmxRKXj!Os!Nj~J$J)qKt|Jyyn0-1c6LB2|xr94$-$$xz5 znNa#R#qs3Hfh)7FHwwWIivw?LEt|LA`rQescF&?55H;4GK(%TW~l^B~1F!eH6q0tMS8KgGGx`E&#JrpC6sk5WuL zn*e(vB($OKDGS^*g3kKYq>wxhC{KC%6~_~b?-gx)jCfzWt$}y}C_s#fWa6kg?POjp} zykJ<3_{aWz5HD-DW|9ATGgM9LO8!DOj+7LdGrhx34Zyt%`_2LhfBsylvF&bY@LbE|X*;{#{q<2_S(^nRnw{i00y~If7RpE;{pn9d zQrb-RoD5208C{A?&Basxh{D?enp_73b_)D38s zgX?sbCmNg^AJ*m<;xk>5ina^%2a_Q!@a|?Ph<>n6u|$h09zl_kM+*pDF=E0deH~p^ zU)qT)hWJ}zf73a%)K`%&EjK99`+gb?%KZoN6cHp84Bml6IzedlKdA6SmP9LPTGu}=ogth-FolX9M_61h{H>8Z{5+3f!jM2Ki$Dd&=oo@jJs{Vv|Cc?)Slc*_X=E3OlVyAGTR&(rq{uSZH@ zkwrBdQAv0Ubx*o>DJMkxMgxTwjKn_>@l9x?lQgVc9n`4MRcENO`?Lb{wu%$H)A_`Z ze;!F$n*cG_G)(_>XJVs=P6uDn%}ndV7HGX zRkld;}bnyiZ7PqWK$`$>s$m)Mq~ZKl3YrT;xAC5R78W-SY6nz%Ad_wtJyef5n%& z1=O=o4iIj%?(A8E|mFQgwrLNJD`7e zpjNV#7?nf7hhl#s{z$wws7bf~??N9RCP+cD`Y_asx91T~1ZMZxtsM}vTjd^yC0_O# zLnVf4{&?Nf|82VCQTDzEuV<>EA6Tk_vjFm7vh zAEGn0YibxI6`Yj(EPu?@exvAryWvA}3i z546@72FsV}$P4w-mA(*iK{9T=zTcu4_{iYSr=~y!eIbySW;ILS?&C;uOID0Auz|;3 zEXvUTir)d0i9@G_wm%Tye*`MF3Stp=P2QyR?gq%nEF-3?ZCJzJ9?0gvm!R+$Q*>g~ z-t!31>uSo1)V|9|(B;)RU&uL23?%4IU=5OQ_eYi-+Y;aW`VV)pe*a@E%Yy!D^ex#f zcc*8w$|c{h%Z+wvc(<#p0KaoD_-uoiJ1HPUL7zXvn-KaWJ-%RDf35Mgkx*S}K7s-o zI*?W^=4%AZR3Wm^Sa~5cg>2A&EMo3lXVm%1-QY8Tn;tT}_S^49@X~f>-tdvE;E(mA zA$^sRLddi(otUpmG7!94H<}_EJ9>Q&ZpY71Py=plMTMHHqXCMPNZ}J_6pW;PL;VNf z)u?h_^tLW<*gYC|e`7M!z$K6T@@qgvS#q}ST*C3D(IN*xAyO{gU_yW69W>v3wp-tc zZSY#Lr8m`GSMfU^Hg6mtCYtiva6xmp~taB=lzZ5NbQwaD_;?Gid=?Q#F6^ha&QDG8V=7P{Nc>5 zzI#cN^vtEgC0dIwM;K!FqVwl!@N77JSH$AEfz-QWi}RN9N~=8#HZJ|(R4G0Jz6ton zyTb_BAakM74dTS+)-!7}qM!HK^rR8sKaA`HB|$u)f4kq|k&M=HdLebRzIb@j-={w_ z_-iXH3g@g9{ch-LObN;B8#x(~*#afhSZQucti%XTV8dUZcpBwi9&4JvR?VWpDc!1( zNkO2FHD|CY5r(HG881}GalzRu=KYb<&=Y$}$z4wy37%=(GT1b|DE|i3-yQkm#T7fhTB|T4fB4u?OnAA({$AFeo_mW-_tjSINS>~w zcMqT63iac13|R{arJQvj`jPz2w*>iEmCKfPg}Xna7L(ioI!T#r14Ol5dW@Zh1#cYh zn`9K!QHkA06*koXBI)(-DEW(QT57-XO2gij ze^zUBH)vvTkT>swvq`3sKLhL<3g^=4SR@w-m`z|1*jtWc3aqbkbeq(`ZiOGohU z^t%rmx)_Qwl(gsKel?h))MoV**cW$?W&-szBsGG?mQDT-03jCdIEP6ndpmn~9$?txqzqVH*GOzaUJ75fA3#x>j^+Vb@T*0Ll}WVz`f%|aar+Q74U{N z1PFF*R#?)D^RmBqHa1*zsN@81v}83_^?8{+>&`6gl!H-A1CDT+K6@~j?${~+v5DM| ztZ`C*7X&wV(a3YecHn$nP76k z|6ub;xj#iSQE2ap$q@I)1$`u(H1Km%UX<>KnFfMM>~_D8h+Boy8#cgF`JTOCaqUD4 zb;xQlj-pN%@6s0T2Xp$2jHR+ke>sH;Mk7lfT)%^dTXUDGR{l+{BmR zd#kO;6;eHYMoc8j>^7X(4%OK9P^2l`c5p_|={!Tt8T306h|Ko;C~>p9e)T#wD&uu6!DPt*rS8xIXARp8`OU;YStmNr=~Na z{+V_osGi+%X9+~Ui=u{=3+qqc7MJKF$>ndWZ%)f@Cq68wpruKP5*P6hHE2qgN9EU6(EpHWummOZPK$+tDKQzy z5r8Hu78^Y>Y&?1e)q1!C{r?|H79s~ayS%KRFhT8R-@nEM`%#`Q9GlDE9X7}9-pndU zIHcWK)`)uC@>vi;TRxrK$E0x8-3Kv`ah;A>i`s-Dzp;~S^dwN9f4yegALLA6Q6-Ak z+_Q*1gXR1H;*B}aXjx1cTL^@=0_e+8=n*`e+N!;BI-%k_L91h1VeL*a0(U2%p%etNVI!?!O(qz|Jn+&5)alM0x~eNk^~9m7SaNal0; za&zea=R6Zqr{zXZf2c-~imT>E1838Q);D=^d0iC-s_v(A5ckEI3v+WSZFZ?szU#xU zQBjZnysimvOvhJA7j?=V^8>(2XQz;RmZqiUV}pBV=@cFr;e|7Z4GK|~RroW)lgB#%q zc+#Bal{x+Y2=HKJ^;^xsEJ&e;1YSOX5(5@xKDV{oMIEAh&>Sm0H^%naJ7nCI7z9^e zjDN4GR9C{bE)vYle3BOub_1bS@|in0r~i0oM~~$S7d=AxNle{EC&e?h?P&^;1T&UF z2oVl=DCg1df7om3KyoXW8Uved`h@xuVNnMv?7y?N)xqTWTAH5xH{ZuPCx9f3#3Tg% zQ`fE`WLV5luEXq|N=@)x&E-XyZFS~YQ}E8Szfcyx&PW_U%J>*u7-I&H`q2~J>VI8= z;RYdauy(sE3o5t#3IABj)+Dlq&)9vqgAIJa?RaMG^N=* z+r+y7T)ZAQ;FxmtQ!taa_}FZT;xk%%P(g@}N<6*7cQ{@0xs$xE*wf%{KLT`)$?Snr zb>xko69N9?R>(^d9lMQKJUR4u#Ma$Uy4ejtmP069R$ zzr+OUHiE?Nb-^T2BQGMT#wT?G1CMyWSH3~F+JPT2Pk+fI63q-ZG6%5Xsni0^XMxAx=_N5$86vP9gEhfYP~djuh6-4KWWX=*8cnpddE~OS zNiN?fVm8GJ8ksy<%Yc|}U=aycpTH~1Vg(&(q*`0{Q0>rZ!3Lry;+4rh7<6+uqQjo7 zu>w=i^J)@LTi(>TA=>^X%1-M8!d04x5-@chQMP&a(;6*I9 zeoGhuxLF;cer93knn~~u@A7NM>k6F@-rPR#+=x~CliT_=nIY-UrktbBedmQGN{l)D zEgMxrH%XxY_d_X8(4&o#Q5LEPTB+b{=u;t@FMkC6jOf|%t!6aqfMxPhgyRzRhX;yt zuPnC(n;e6>7PFTaN;L%l8D%-tJYAWMPZ0)H1Ai{5b!oL>WsK@|*Iqvh{EmmtH^z0Q zl|dQzwL76Q#Lk0O4ueK}sZ(vJ1JbN5uPvPxr^NG7g)ctwU{Ykl2=_6n9H7!kxk}A- zSAXx~>^*1>WV6p+x-7G9iC_kLc#K#c!0wp}%3lA2cesKAAy2`qIQyA+hrWa?v$Rs1`P%YWZfdFonKq<@ig74tdGCS+dBdwvZVo5j^g zl5CW&z|SAuZ>-(=vw|2Lk>!g2{;3Ud?f3%-4l~V4%oUM2du>;m3V9zCt#L&C?k#`$ zJ8cXTVf&$4pYx2Xn1b|calNI{#@gCzH7wPT3)_xdfZ0>8;Z5dMj_5v=5FFq9?|b?4TSq zK9uv=-CEqF=5xekP?Z^p)~KV55)b~9_x&Pwrk;AU%5HmxPEx_-`8I>md z{?&V@Rx8;8eX(hQ5!>bn3q!EmTwa0{O0H;t-|gA#d3UW{hIw|2pE6#4Y_$R3on|Gu zc7>N=Yr4liaYIu3n0XzVIdmW<9Q!eKiph&AZCM%BE=lg+@=}O0mGkb_Lw|=^5jXJg zt^{u`2U|TDud@aHW_j*Ue)A~?%%C}KkA;uIHj_Nw4zy203K z+;j_nkKj@wOtkkI<=J&0C57v|*18j-G-UYE>^_6I!klqrAZ<{P$hY&NxS)ui2`PO~ z-&3^;vw3?-rCDAv#2bbYwtt<;e`#-A0DDj=`#1AK_0(rORlE_MI|@vR$gjh5%c=s` zOO!Ndz9$8kRD7a&2a#5?HWAR)N@a%Yj!J97JD|7|bTdbrKkg>|t?TX7Xp0zJ6M_hJ zxPGKf$mt%`WrRE|u_Izn`RkSvY0sQ9s5 zS^ROto4Iq7?U+jm>7X22>c#3}ztoX*`Ue~PT=w&1!>L5iT*Zcub6ouii?S@I?sz_a z3-9_}YRRdS8O9o)FMpMq1AZ)(oHe>gU50MPoRSL&>(UjviN0Q@*i?V`uKK{?34=}< z6vt`a+Do&!)`l1~f^@Uth-rGC1YR-P3YpA zqVzm|_llfGUVr07xx0FMOHF79ir(OrWG++x&rwN*l94zspEf@v%{|?mc9;<(N3qs$ zWt$~u#0iKSV>m?k;pnli!!Eb2=NpBq#f-wq?*iU{V6XeR^34(|XdaH<-aGR@ppeTe z9-D?vts8ZhBBjt8YsGZ5y~i*ITZ#q)k*BOC?ftz|IDhLtuhSkI7Aqyt&lk!qR)|k2^i%8{2J+7cEPuZahq~wCO|1V5p&r88E<_lX|H)s zh8R@P>tAPwT{McDta8IEZhQ7Su7P-aa=~<6>(&0z=f~Iyl9Z4%e&p>n>eMuMXSf5) z3Hewg`F~&=T{9zYP}{EucQCHPfSiy2A>X$w!*@L}HoKk5vuEi`F-c6EP*Esy9nOl@ zeY2bJFw@!a(T&m!jfzw%%8PL8?yZeWfL;iuJbASYeQo*34RgRRI`-BhT%u{xdj7BB z?Hx9W==SRvMzd)qhiMtB(i?QkVA})RMpb+X^?zAlbuMMW1*VneBiRYWYeBq!W1dYW z3gE*u3PKzvuPYcHv>ZiF@9eW4G)@)1ZxTNgJA5sOTHl8D99rWxMo7WLHSc+TQ8I9TG>|3k}WN$w{e(a{BeMrh&&jIY)9U zvdZ<51#`v}Tg6x-W=7nt7h7cUbs1b{j(^cu;6-hM=xmF7YDA_e(^7j2K@|AA{-NDc zne5^+4+8&wHT$3K(UMK=>$}Z@?5Apvb7aSszBizXb@-9n)Xrzik`JUkdN#!VNxmgp za?1Fp&Nuaw`6@SFZCTIZxSjRKFAiAF1$`~*`OZP}!HAKFv&6g} zWy-9&d4}<7Q-8yzGbZjA1jZ!qq6!&0m^YgLg4uIE98G9EBP)+1EJxvik? zn89)g#o?+r0C=7LeS{7)qo|m&yML>>6a7hRX#-q4SZOcWLwD+|wuKkqFsjicji5?yCX2>b;FV$RFxv2De#0RqX#wGR$&%6xf!9!e;(uz#_m@s$ z-YX@MK&&1!Tw|>{?;hAi=hj65O9=h{l{Uz)t+BCU)2YXvH|&oj&Old#Lj zYF(p>U4~xQb7)nCbHju)9e-hSqqRVM%ghys_V_nz?nNQ^Czh<6WLb$=#fN`&k5$i(fb zF53DhjnLbI8>RVL`tZ76%$DS`An4gDfA!5c&SrF-M{^!>-M4aMsu2%(IrhH$mojI^ z76-PH*E-@O2@Y>}Koo(xJB1Y_R6`wu&udPzI#=^?2h-9&npPz33r*8bm0P9FuNueR z$pVP5BsI@LbgHd}(0{`yKqxL*UsIvLQ_f_<5S)nBquSq*RE+*zW5O}SrsnQWT)p99 z;b`_9b`a!TOy7xeh+gH%q>Wxtn2Xc8#wA5AnI-5F!7+_D8+5pN?0lp|yuM{+h-EFc z?s@IMCj!`s*6&;6wxy{B3a8P^%a`5Lw)AeB11OGQ8CSOzt$$v4FLwtaDVMWnV!^gH z^(wJW%|!aC9*`?q=#CCzslg}fJP`ZE-)f3lx-^^b>HJ?`Q#Dw%gS$v#UH@q;#DJ+! z=JB@AKrTK^{LFX#8bpu`(mo&F^y4MFX_{P4_$Z(#7+x&X7=|{*>;6*;n4cTZNiBa7XkhZ|&pt`QKLtvuZNo9@o?lnigGL z^yi%|Xj zN*#iF0u0T;cdd1>09L_$>Q1V^y&gD*QkgMz(x2bm=w;RA$#2K8|KproKth>yQf}8l z{Cxoru#;goHoJ}V7x95yOHTU{H9D?@mkcYm-+x?=MK|0noeuvL5EYSdUkyr3TloQw zJ*gZ%3zDt2k?Ufw*kp3E#vD4RJTI&itPej*;}mDfk77gn5<49Ek@W$-*mMwAZ~Yag zOlQ4B2&a^C)1utKO!1o;y{we2`7&Qi;_NqjQ}9~q8^^aBR;@=R>H7g-!5zjsBvD>; z-G2t);kR^(Lkb<#hBL%^sOCTYVh)r-nz*|Y#AL&sm|us6Lq@OAKz>i-Oi%QEthk15 zz(v%XxeXO^Fvk!5xlLj4+r|o&B{s|iTXR&T5AudaeqNgTq2Bb|OsJ()T?VFkI=V$2 zLm$olMs!+V+i{0abFwPfxTbT8dD6BWOnkTm{Yyrx<|9Z5XRWtqlBffa#H_4xO;UYjcLTcIk0kd0 z8s7Cow@W>PaJ}F7lzXSzPjM}SeXcg&XZQRO27zen%YI5)>ZNgMOI2m+&jt4%RjNi3><@0pGQs^J zmkrJ5vMOQGM#t}iQVaX9v7>Q$&40xYYf<(r6E`6OBglm`Co!z|lLcVrJc+=oy4<%_ zCv5k_+f}@LqZPnIEK7=BfR6C4(1Vxm$bgh^P%my(9k$( z$`|cAUzQJKlW6g>Tj>N#qxHV-bh~-Of+~@J5i^-EH+*StZS<(Ms5U7FKEummk3XVpz)6LRGELasu;Dym47{z)_?R zyd9u%V1PHTUW_Q+!UjO=&VR_uLYE5^+7>H-xpk&^eM4d9ymkIK4MUw>mG_Q!g|!?5 z-GOo;Se*)*zSrZBd~;x4tHYz#MS?k59tEw0F&r@}3dZ!$7HbXrgag{#x+VTsf8=Z< z)qIOr$O}|DmED*0aJ_(f_Y!KS>1@R=V!|l5qZV&ZtJ;e3_i*&eVt-YWtfZWJM}PBM zlHO0=qX*@)A@r+vRy(*&V!lQZ#Vk_=1`a(Fco9d0N=|}n>{cIUKGRrZ*VS5L{D_oG z{Mp@wUh>;}dAE;B6)g6;1Vh@pi@0qG_oa=;1B*Iej&6Yq<91F{WmF>s)Sli=P=!m> zpLs24)fRZqFe}?ThJU}}h38Z}Eqx5V*zSqhHixYuD;xo;`I=ws7zXhpz)@{Kdtc=n z+B3!d6m%=Jdhgw0rV8$9K}Ucj=|@R|MDpj`0b%We*W7`zf{pkOz_(YHC@$YrT4YpQ z-#5;ely7si7?600*tQW61)ZI$$~SL>;id~D`)UZP+I9EhCx1oji`^Prw81{}`#+JK z%DZynLI@2cUBW=)j3(EfxxRTr+((JFeuLiD*k>s3Q0inr<%=jT0a6+`DR)*vC9W(B znd6IsP`NB}%(s*lF9C&)O6#NEtZ5AF+FK=SCxKEU$;KbBf~Wzi7`|!}1M4Krt- z`X!IYUKp!}Fn@SE=_IieyQN$aCIG*RxwL_RKv%_)3chz}Pg zcbjWUwod{}E#>0dtndZ56?F7ey{pZl7*tVx4VAb+#DAUEWu5)k7~@am=m7rTEyCy4 zvHg7rg^`wW+woI~L(C|O#;-?{Bqy=oq9ZyM5wJr0+Tg$%+k(+Hjg)H90 zfPi_hl7FRryZpBTdssUDoZ$X5N6mnepmJsy7&07VXrG4> z$IC>9Eken^%O%EBOYk8u!%}L9oS4)l9vb=%liM3a+)#bsMd?*o>i#(5Zhs^iPm*kXp!qGjbf;1JO+-RS9xM&X zQS4zAo&#NMZ4n!_kkWK#>4wU4HnzDwZFo(e#!c)lIDwMUNM7l}RCbd;;_y$|L)~msD3+XDg~FP6m7MfS|7SEHc08do@2|E^*OFN zGJm8uaOyu-i3^(-EiGItwJPZu+R7wh7T+I#4=gX`A~xzj`g3RVh96r5pAdSBjEK3Z@YLi6&eJ}O`Zte%EfiL*}RB{t;mQEg^!G%Fp zrSx0gzVy{cB@aaa!mo8uS=vWr*729gw|@}d!!7g^Y^CtV8J-HcA07X?fQA-5wtj=U zvJ@U=uKdv^vagmXyTpc->pnvp@j(`|;CEd*dL4U52lfHZ-GUqYUFBH!6Ui(|IjQB1 zpks;e@g#mvQiLD)8R($#&9y1$t7zhXyKI;C*?89%eE)qj6< zSPJhwO4mP++DbF1&&XGF~=}3SNyQ;l1&P`I86Yya!p{S&-8W= z@!l7h{XK0N1%b_-RVUi{q53XO!hhvub?Es!o2FucX9Hrw8GrjyPYRhF@E|)nPp-e) zllqW=hNMy77<=q8S&5vxgfjSfTUI#?(6%~ij(OnJF4XBiy5RxQRx^(iJI(>LcSL6t zP8(Hngk&EBBZfJEH1Dd~H~NR`4@pW_hQf8hF$OGEWi>T>;AwsTcr!-fFn=>s{^--x zZbfr93elEDk)h}rMlYtpt&CbB*0$!W?gFZPiSce{g{=CIuZ?qFsO-TZ8wZXUj* z3ku@u3thEu(-pD{wOgYVk)mJmBs*%kTB1%#*#3}w>VFatlVH~UWX?&-EmQi9>l_JW z&URu?x5wCouJXO#>jWW`GJiiy_=1{hxs6mQi_4WdMl)8;}m81g0R)fts*G|8Ro`1VZo;m`_u!LQJ z3QHsmG$Ok2ob4%aNoP8kZql=uzWV>~{{o1CP}h2aSdPHcq!s@08tCB`K@P11%HuY9z&amxKX>6lbpv3t(8r6bDY~va{5~tAf%WB17Z(k!URXX zo!yNKSt*l$wouyD6h*TwRa@K4J^;&>XlNE#yrJZ8&REfE4$qUGI!;2UelS z7CGSQclc)M5`C75tqu=2dT0B)TE>REJPjGZwa%*`*R`QeVO2~NB$#kTs~S#7Gg~p$ z17=Z^s!nmoy?;*8yb;xUCinwx&j1a7r?M@#;$$zl9_X6lxPVgk&{GPs$QP&M^ipy1 z*TY9i&a&pn$_X@h*JBKMB$3%KPn&$LRr#%^CNwtGm2M@@LJx_**>4eTZ4}P3B+X31 z-6_q5QSQ$&7pXJx!YGfabRmgWqdxxNg7V;xy^YeDhkr0HSZz+I2S)hO%~bi;QYEID z6xfWcfO)5ffhv%IlWN9lb---@9`a3&OhUpV*Y7^I8s|LLXz7t`CV5GrHTIiQ97TLb z&{ZNcndACuYhpHd69r0R&e`b@PtVh7Y0Z(|&dAdCz?s$1%+6H|ORo4{EZ64RT_mya zv!=yH>3>>rh$S+0q)&xkxyQbtS$few4`ntOK^3R)x2I*ZQQMQ#^ny2MUc_^#ehuTU zoUtoJCHUQ!(m(kIha)KylIExDX_d^Cbb#e{ZGy2U(J1kY6a`my-xiE_q98)AdpKX~ zna$?ui)dU_=OhKA$oEM063g`r#CJpHj3be4SbvlR)knNXOP@tvlW;L$w|Ib}nl1BU za7leA`U5a+ysY^bW(o+^i=2V-3Ad6~am6E3dg`SmX%S9DsM-E} zndZ|i#}tOX%l5bBqsbo5z(K7xEMVXiRdecBJeUk0ll!<09LU9~5}y&m;QXJhh!@JJ z6@Skat;y(FnaE1`IzzciL1l2a2T~nMH8scL^v_ZQJK5?HFm~rZ>hfp$G25X{pG15R zK#a5x#sJYIh?jZOy*D{@1YZkf!um(|*CWN0vN>3y#~I9m?1CoBr6s>AP>2G0Zoc)j z1qq36#@QZ%-LYTQMivBQzvOBOFJ`Pw!GEnlUZ`5{(Z_>b5p7Uu^ovgbsn)7M%dR9a z`9U)j+t!4DI3q-9_{KT;cpNDMxQo8%zU_JmkJ%6uD>Pn&Mqm|&;3GteUe!l!5LQF<&x*p!X32lnGmPU#f36yW`Bp&NNg6Az|U&MW4!$O|;Fq&wNdY7Lq>`J3z{SC{?1pOMG_16?r(Vtr+6|4R>u)!TWA)8}+@4%OIK zQ?dBPMJJ?*M<_q5>8`Ig7JpSAWb?|%*(ASy26!-ccJ>zPQn0y22ecMSa{ydR9%rns z<}PAU#v1#V)cK;;+l8>ntd7VxO1HJD1XiobJ|8y9`}v4wUk^`WQ@WQ2$dR(xgK=p- z@{pv^z4l1fDD9~2L=^eO8qGbl zNmCm&c3ux0qJKtDTo0b&rvAU3Yj=>c`nR72n?KlqlQXy0h-NAQsxbER%@`RcVirm< zuvA5^Qd)7XP`=rDc};V~W%tns})|6r2WXbHrz5!JaW5hz^JxML2p%WFcy zqFexiR)3=<^)J|>NgR-@zSI_3$nB!j#Yh_ z9SA<}f$bmV^4-Uz^0{Xxc8gFkUC(G*sYxv^$~@`|XFID&)qR{k-mn;45TC_EOUdw*GcOp4L(eDJQXf)TJV*=J)DnLaor^Wk>+ z0D(PBv$680u8#FEAWPI{c5+S~_FP=6wsCCC1Qe_4JF;dlzUDF9B9JXViWV{dZVYhcpmrOWfMqPDq2|7l2LAp#hks-) z<_+)kXNOCgt(zJe70wgkIY*zI75wwAq@aNMC6iY6^|AisbLEP1`RgIKb(Ms?E!f{u zkBtfyu=$2)RZih?3@r&d=kH_Xd|xb19A&uLUv6xeN44{TjeS%7J7i^8&-mGe^9}>z zW&{U&Nquynt)BL$vUO2Dv!w8Yoqwuanm@IPUoKow+J~Js36jM~K}86up`d8^@{Rii zRVSZufuiSBw@fd|DzcBD*8W3u&Y79ybkMQOU=oOGALxd~h%>596vwy=r5>G|7>5_4hxIP4=Iu?f;sMqXfO{< zQo-n{b6-Co`JLdY#t&l4h<}eUXuK4%d}uxp&_yF57a)Llcn(4}eYE+C+?tZ=HWp`y z4;)a!b0y_g)3Z-Z&xb=NhmnigOb{Cf1%uXnKK)bE95H^30fok0Je3_PWp^oz*tf>O zR8Sg!jG?Oj=Ly}i!r-R+BrI(t4fXO|*9%pc8jBhThKI$^x?IYSWq+lR28NRFNHiw1 z-4*g8jD9*OmEIMAUo5CIc{T2;H<+Qe`bj~*_gmQu_k-d7fuRAN7b)~BT2m|RnOW!i z2(pB`!Z(;|CE>-o?3qHjgktY~Bs|cn`lHj8qCaEA$n|n<&8JBII6JrkvAWhljB|Ju9 zfrrlFbwb(Dr@@27ItoI?TRPmD{_$NTnNlyvl1xHC9?Vhls((HBu$hN)2FWH@dMKg+ zKKJvo!4d`GLrPpVtppD7XqN7S5#f}{!Fky(M9$Umcgm-5oY6Dz%ZR5iX-u$cGQ3-7 ziaNG>Cw_eb@#w*`d2tx7QnpXZm5NS&N3bi!(hPsPlTLWIh@>Y<1cuIqT*4iGHMS_0 zj%N_ALni(69e?zxrqiqLu*8?oOI}a*phXG_b84r&N%X!>Dq=Ah`-!!Q;aNbW;Int) z+mSMz%L{`_4=wU^lZq7b0x%fBPgT58i#BaY+rYbJa~wGrZEa&HPt09Em?4FgN!asD zWF>ThhWX@yTatY>b$a?n{~PHO2DN|%y>%N*3{FxJ4S!HB_%T@C;9$!iCX!R&ph1q0 zV+?{(e2LMxg6LL5NE>!G#|LYwV*a7B>- z+<~=B1G@rx_H7I3q`B?vo3=Kq z_)Q;rIgV;skbcjIfKjPbkf6rQ%%nVt*_pUkl@#06iZO=RCPzRI-W4RqRTVC;JJXt&!fz-y~`@FTXOF}*`&e;3<`6JB4;fZnjkv5Q1IlC~r3PShrGZ~&#aKxX{7Y<~lh z2kkcvQVA^3Cy?z%D0bTx>jp{@p(w&XJkf;OqbQx!=MzJLhdaU95ao{4wI6hZDxMSpPtFoWz{ z>VwDR_emvbVtwoeXo>&1wJ#swGc(gaH9J~pf~3D!0~8eX1~|3;Y`vqZ`pyTVTTVbQ zUf#pmt`(R^gB_JJk8_?FQsge4_UEf0%ENQJ3*Ka1PPadX-f}pNjc&BxC>`x96Tm2+ zF9M5VTv$kIoj(Zy^U6YD?|)fzDlHymN+Xvrj|bl%*_89MQbWS>TIZQGG^QIfQul5f zliZH^rP+hX!i5t|FltrSXK>>)0+$c!2aAh`>4!4PVH*aj87H9&79m`4i42}m)j%cc z2amXMc)&xL;QY^Z>WGdMmfYD_vyHuQ!n_zf>~ zcns~>UDo}?t+Gr_CDMO3gBmfNzB2}6iXao{h9Er&lh0OrNPk*kCk?aqcA?*7w10WF zrdDo+oYV34{FCX#nSr?~x*M=x+O;wH4R?~q<-l#L(j^@9uuPm4YO4b);R+Ls?3DeZ zvQ5@!UYitMYK5ICaMSf}i*@Vc342k@O#F(C6?9b}X@JPdJ!@g}@%_$=Vo9FHr9+eg zu!}ZOf?s$H?0;5{qmPXRHUoDX?6bWBMJcJjWn)hku6L^$y5KTn<&C@^*rZEh-2hX) zH}Y|w#S|q`TSFb-Hdlhmf)Mup@p;MRplIvtQ#ijqRfB@#Sb2 z_JM|M`KFOgCzA=ii2K}S_Fn}heDvc0I%681BquQQ>VH740RShKM16|zsn{u-2x0z* zXs@C$A5gB$F@Mj+%_Y0p<}&z7Q&B3N%cxo=&k10ON~#BQaxupgPhVIy;6gV5?dxP| z6ng|rGr{vib5+*}H6ZmN-FDO1{U7$;u}!kASr_h7mu=fdm(4D#%XXLT%CcS6W!tuG z+qP}K-G6KC_v~}lv-bG~=gRz&SI(I+#vL&tVvLL#H$IlwJJm_EsjY88Ol@-GFP*qd zG|cFUKePPa1s~2w4o=B2V-|SvQ4B;I569Rd`Q0n+n+e|63}?pD)BRpHzPX(MJ*?%(JY}qP|!r(1dr@#Kr2#}*Z(PmA& zg4YNSLzy<$l!(L|rj7#{dr2PiBUcCzW+y5kn`d>WX89K8oQh>|$0|P*7n_sG(`N69 z&r7|%>9YC4i6gfwLQHp(*NkHMpFzWsCt~UIuptks4KJ;#gzutxEg@k1o<6$_Q=~R` z5r4l~!24m7r>*y5a!UQxpoCx7OMUx}8eAN$;r$e=`f@9*!kdv0QKO-L$C<5cP>~*0 zJYl(h_w(I=yn!hQ#&R7QL3zCGa@+E!RYC)>fl4uHK5o-4ed_g*ms55{cSzoxy#Aa@ z?JO~veJSx=vqH8GK7#mbc+_qGOM?V$>V1lFFSilL4ghw|yOjX-NTXgdqZ>kPUk7tzzNdsa95R zsRFx4LKB<>`xet5t-BA(X+x!sH>1Ll(Q#C@zu}vkcLs@94qEO8?Vd764S(!~&t(my zE%3o2zkn^WQuxe$uLsrNc>YG^&W(g>*HE-@LZxhR!*7%xBN~u{_#^yBeF>zg{0mM8 zG`2%}nd#Z^59ku=Xo$;HGK$ocpD74M_XyjjGgsl>wq1rtiuNc5*?0AMNjvgXOJ#DL z_HjEorhk>Ddn3GmyMEJzn19x6w-hoi*iMl(-tk|()eU9S1-uKs={;XRSzLNm6}^E> ztKEztBfB9JKBvhgo<#Cl50p4c#~KMs4z{?FTQyrVH&|IK8A>>BGC`RZ8_%ssFgQ>C za_PLPbTOX2ls8)U3ulMc=Fo%saNhZmm*U>3U4hH<1M&TP(F;Uh+kdSj!*F=^8?;sF zylBCNUEt)}K=W zX#|9v&TurI0<8tWd4J`cz%~0N->&VpJ#q{y1zh~X`qS#l?e)b(5d7F>>CWM_ZY954 z3a0Zq(@mFc1{Yl-Jp3ELU8)kexwZ(GZKE|N-zTua&A7@5V}R(9P8Si-^YBp7iFc$t z(*X8K(}aVM$^%*23by;2uiLFQxRq^c09SY1!W=mNoDvhiaDR_*F2?xhAEI46zBiPl zi0u6+vFmS$)y246Z$B`WXX+)aerJbC1zI_K2qat(z%O|gNhWWs!3j4hNl~pb8DCDu z1H7M?PMe30rGt4%f2F0j13}Gl061Ucd0?Prl4PF%NyfsxccNV$+{@W)ybh7S0r&v* zKdKx?-|va_xPMCSMltEScHY}wku}zRs(#mfRAn?-#lNcbrJmY>L20=x8RiOz3dX1B z=0f*H7Acli1-ltOd_tL1kq{%@gQC{icnOk5$k~cp$#@+EW%RYyTW%d2ZaxjxQ=T6i z++6(l%BSQ3(=HQaA85E+cpEtw&^kE!u0eo5nXS!noJuhPVW>( zI|4*vuUaN6!ohXi$x0X{3Lhya=U`^V6FbXaPu^uw0};MRem6>cY2`n!v^_N5U6D1> zQ3P0yCrs>NAR3ZQS0w*85dJ#eaU)1$*cZ`Ny+VaMGuMo+4((9Iwai zqBimZ19?x+R@9A>@-~rQZ3GZUNH2ujzT{up34<4IQjhEhd+T3tpMAgkk^mbVJ0Z<%ztPTLEuaRj#IM*B5acxue7?xT#rh&=Pfi4xuLpI1D^Om8>`Q+T!>AFn zPk)pI{>xhf5Pz?SuhNpeSI930Ghod*%oc3+R&VybMq|hIZIu^0&piqANy(0AOtG^u zJtv~&YL%K16$51G2VRO`e)3VnyScShF9Wo+O^4m(OLZd2FWTv%s6_V{o$hf*O&+eN zT0fn?Ib@t%xA)HaI@UwWEocX44;#dwW`FwJs(~&IgS-+?U}PM|;Kk=6>yh%u=)Bqv z*5f{}-_)0PQo$h3u^3aJ` z7n%36;=wvZd-GaSj4}778e8xlNBTicX{$o2%CvBm?H&s&b}8%ZQMl?bxG}J~QHK&$-U^|E z`k7e%aV(rd>M=>&ek^j>^QX>NOMf+!<;5b8+|Y?buyvcj=HV#}wesHaiQD0tvTCg3 z_NwFd( zLpb=!d#-)7lXI(bXS2!>cfB(SAc;GeOZcnk{tFOa8Xoq>(ldcS(V+rs_J8JEJ3WI* zD4Z-eJ6x_9sU@r*W0fiue&jh^KnUwg_mQ3+>)-e z7HUz}650veeK|Lc^7GYVc3vk%TEKg6oMTRhvMpy-#CP)Iz^?CN20h-U5^&SG7Pn(z zdP7*NyRMwwnfz3r=50POB-!}c zd;_4VOo8VqC4IQ*Bqyx(azPV#wXx#xw7DDF^{RDWjKT3Lm@T2c)7 zX(i9QB1yn-xLka+IqA!(LAkMZjjkr=neNOc=>;Pz4yP8XU~&OE^z4W+v;BU{$ZB$< zCw82x07{qSkgt&A7mJPVGZdGF64oPnV4BEevKt&cKGp}tj14{+y!2GfQZ<1AKbq_n zc@(!h84}J+mh&reYJV$X*Ghk400bJdyCLd0yl_QYO;PUYGqY=dS9tP_bFLbyd^}9pZd|PBH@}R_s^_WV%t@Ic zY(1&bmnN<-kvI2xJ(P)y15;q@wcY2ovF5!IBkMk~>Xy{FG=D60ZIHxs`gtRq#`y$g zYYRFoSd?oF8GT!dJ)6$Fjg$lv2eJl>RS_hETrE3@!Qw_&E6*3_zc6LtiM?1cMc%;B8juL?}XtJWCey%k4Fr z|3-{Gd`lw-$bY#&T9tQz9IN+V)u-O-5H9a{X{>u@FcPte$0e!;6yq*Ti zs)gk2-MnvgIEOkNSYEG-GG*$E7z@|%_+zI$s^MeCk6ZT2WuGM)Y+Kob&bpf)Zbm_m z@+Y?uDu>(QziRuiwCF9Hk!fXIDH9gMpIeJcw8a80;(u$BQ}$=fqSjRh^}~g7NHGKI zHCj0&gIo}XWH5sz3&lQs#FN6VqR*DEiQnLiRLHlxrAwKR=-kM1a9EKu$~U_&;WjIO&9KZ{Rr*WnME;ypA?KK09{C_V zrPW$=4S(L?KxWv-4QgWk=u)HlyY0&man>T3K8I+YRDaEhYit0dOdTz(B*V8`6vKi7 zy^}l^kIp9L8t!6T$x#c{wmQ7qjuJuXa%Ax_%*6D33o7HnTFD{+nS~(K(L;|@+p8@{ zhDQMc^&)F_IW0mwnWMJCOFF;}&Z?bG_Uz?kfqyS65AX|x5&Y$i^HN4?i2b9>WF>^> zPgz;_0f<6S*6@P&O2Y`A`jlQV=#JF&(|h%!fQw3R0dtZr4kyS3tqusU<^Ec!kB$zf zYGgCe!7=*%JdMR+gdbVAN-cOt!A?~KCN;?M&TNd!c{% zG=Jo%_+u5(qghDBZSCdt@l}GQPh~yPikz4S@5>$ziRL}0gQ#MkBO%ydXAtX%-d{3smGOHk-=QiOkkhX0z&Rj@MpfH8 zH+=l``fkwI55Y^}fs=}jJ{rr;U!NRePs6oj0{Mo{wnkc%C|2lO7}pnsmy z3Lr3@t3HvRT5vkd99=Fe#MdQ_x)@9Wh2QCF(n>diZprsQwQb~;i3O#4W%=KX=ou`P zoUB(@m)Zf89_M$?sfYP-4!wg@hkLgu;x&K4qzUQru6^*vw6zJgnAOvwm~h68CgOA% z$B_d=2z6Thc@|458#3{Wyg+ZJ34bMgR94h21rjX`2(1>+7VROFnZTz!bji`4(L`Oc z6g)y&hUHtUoTizOYdkLMS$$=bkP@|QhJ!vC-be3Gj5{|PDld2))XpVGs@Mno6h`!A z$X=5Be3`$R=Kz*!igSTJwI1M`xF>kkXO49taM*5G(ijhXbyGz1B6d&3wtrV{M1%(- z8w4CflE1f@(m4p0WJ9r83qf)oXch^M*A>SaJ*-#ZIG01k!?T;5*?&SjTDf*&mDt!J z%8O@mVuY}Hl2p`TAcDPM%xTnv;96@MZ{N1}4kyLJ0-q*{b zNw@*-*>rI0arYB4j1aH&QGd;LcOBbklmrNX-1IS`*K`sqpx?LHQ{#LsJuC@uKFJNJ zBJh<%kNoYTz%E~#zP^w?48}8CAOBS*?p+mHu=R8k76aUU2VswmMvpUShKd=4hkM|Y zYt5d{fe4xSsZt+F@6(k1$v~n>^a*cvr$&E=nM|I6Z{y*pg+$@N2!C}CA&fJi^#ocD zG2~Mfy^><{JX+%D0jbCWgAw!u{n4-2K+?m#D`Dp`ZS==W7C`Kgz!@A{$__0wR~eyZ zEZ?~+>$bz?29j@&!bgglQRRD%eq9aoCdb2zD)nM};x8eFA6$I$BoB?G&)i%b9de*j zhL9VtB%nzjP$oe!^MAH1SH~ANH=DurK}ttFIvBP;S2@Tt3OP17S` zRvp4XFpU|`5XptQ(QdwVSp?r3Jk91m5lgfHV7h}x$y7RrS%0qoIfOL~bqya_ctk#5 zQtZ?ZTIjUK0bc+oL+5Gx=awUd&Z>Cz!hiv>~jOBU^UXhuB`> zxS+8e86BtdmxM}F{bRpv`VTsDBCMmg3+h?Mn0(uoHmA0lb6yM6xX(v-n;Vxk&TBPg z7m>je58RZmB|s#3IlzB^o?alC`w(9qlEL$~oWJQ1_QRnoNr4^52q-mPr{iq@ar$&3 zs9A`P7b|N3KU{iD*bJSh>x^9K0vp)<%hwXG+lhuK7%$f7nk^zN;z?4KmNuTNgOj|N zIYkd^^y0iH|7tar3x3ue0rdC8cH%$%2u&Oi%8A#6ZF-;{S!aKh&=cV%JN6Nkzq|LQ z4yOAxK`+@|VKq0_izTs;0Sog}0{7GgM$LbY$YzFobw@4XbQ;APIj+|OtUqG(K@}X5 zzz$SSj&)lp6PLEl&`Huc!XX?bS`7?Zj|n>&@y6BBsQdn*hVaukFffPN+mr&4F%e`(9P5`~i!(SCwMTO2GraSOfe>){v94<+wOP zR`!LGm*i3#sIy$0+DuGy(myOS$o5Ho#Z@;a$Z&Wu+;*w@I|5XO`PsIrNlh2~!w(lI zFQ3h6D%X$dIeC*euZIZ`-Vq|N-AQA%c5)ID1VJQVAS8c+Y(EO!?BMsii3;b9Wgec% z)0fVZ<8(JClvZaqCms=bIfUp90YO+25`IB(Qp;cKXP<1B2^Nyy2_T0Ll08^o6N)#J zT}Nv>lObbUGN`FdjPKL*%>k|AH{60WNbh>QHYcTC_i2L8QZnyBX+c}~+$*~;PJ?k7 z)zM!dpMZaXNS!EvXt=C6o#L&dv%^RWm1t`7eb2Oz)@pZoKsmgN9n({=EM@r=P&C>gUmfA%DXy^Wp9S;p;+4hfyIMIIZXJpZS-Dk>W&(Tf7tW$Jm9sb zv&MG$O!(>fxT;YfIpgz#ZZ3uMKy0{GlsgJ&TQj?<-Eju{P4Uf4jFy^Ny%kJVVodjp zVgshWj4qwrCmWL3HWxz2tuuqCLml=PQ+R(9Lc-4aPb&I58j2`>X>VM&cC3>n5S_^L z`@(h-jUe%3WU(wpXP^yop!1W&JXmW2$+pyA0jUXw<9f!W3~5#c2u}ZHEO>gL(*{O!G9ez(Gwds zW=Y{HsBK`TUSD`^imSCXdKgcUTl#+$$k_n;gB&>HGkCcw4%h#A>3AG_N&$=KzDY-z zNYL61*3;I{%?vT+*3w;E`PUY<0(ozpJtINq#q>91h%`GcUt``aW@W;as{{GOtYSm> zlo6;D=n>i{kx{cEwstsxl)_}89(vaf_%Oo|4q{iHHCwWqXESFNsL$aClE8oL@K+y4IdDq|rI@?PtJ ztJ$dVzIhdr5L^HCmD9r|!UYH1ePj4G*u%S5A2N?)j&6XM-hHtPg=e~;;|f^%RTf0} z5gXd8q;u|o%MObEO*;X4RNH^oS@5v@xVL4FXJJYsh+w@XS8ra31K)aH`MgeQdZG`9+!NmIdC3~~&Y=#b!_+bu)v65p4oLWPLX2+oPgtN`uRVAA!I{SQTH_E5 zeV^u^xyqmI@0ngFYBl<&&{xnAH@GP3n+d4exxap=RO6g`@%9}8*b9Gd{RR+29jY%4 zG$4~Y!+^r^+TZR4m=2T(!7p_ennoSOj#} zqkAHJFe;+kd@@q>vrl&}x#U`JWB2Lj=5Ecp?d7Dwj`8-$osOA(yZn<~!K9@LFiK~d zXnMugEM8FNE$|lZk)%CAZDGkkxEP_00yH zW%Ax-_&U74pvjZhJmS3pmHhQl!LKVgI-0PfL8Yl8#6U<0z>tKLN7%7}w1fS?O@3=3 z0}380Z-iL=IW9$I3>*$N7<8$>f(`Ovhxn%4H&IcvNuAzr>D8K)%i3Yw(jlTGukf)GlRZVA?$2SrId`yeRjVy;BK6Fjubw|$MavIetPRok!+#}su#5f z!s6$6Zuior-LLnW%{LO@qjRL1%dSHix!#Q+&9!^skBNUdvt^!uX_#GfMB{Sk5yR{pm0ta4lZQ~Xk$otlvl0-lEfrI)#<94CD6`eH*YMx#M<^>WCoHr6W; zTG8{sJvV=n6A5Y84k7DlpWoIE+l=(P(YZre3#fH@YS_)n%xqXykUy{WQGZejolu}# zF|+3_OSt*w5o-S8pb5cKiB~s$iB2U_$JsAFw)ivA^Vd%P*^nJ=`Aq@zH_<>2?RDyr zlk(_$=#R+2aeNo9efvR}cTvKlk-;r=JaVJjP;Y-;mG9PIGiB)x4ES$&^*08A`3=rA zYWOUNvP@(0W$KpukAZ=Mf4E_~79r#2_C}%noO5foRfEvKebYywlW7+U6pCEfxu7Ld z)y(97+x~zF9|@g+*bcy*)OG@3+3`NaggnseqSw*cDbe9-sHFXfzWZ>=Cg|-$fA+PpL0|Rv#M054 zn2@6(<9dU8CHthsNZieA3kz{{(7iO$KwUW%lHd|R!@e0l@iB56>t6!k&rxbR@F{=$ zD`kodT2WP--$LK^BcrNH4K>K~?2L0@U||Nw^i&AAOy81+`PpQYU?Z660BC3iN^BEG zuQg~4I|`s_?rd*8FIPt5*|i}=(ncahI#&!*;2tK|AoX@8ThH)A!k>o?l`(g~n*-mh z!;U!6(eP6a=?M;lAmZlV=hmDaoYsG#OKSFsR=_3=UiIE?5R*b#>&_a3(yzm?C`#)- zumbOfd1wyD=>8DeIPm(oDZ*+mdGg(qvO25lOMter@531mrRk1*V`0%7eUNDTkRf!s zIBC9wHQOHuGa=2<5zHcUQ({xzO3nGy9q#^g!1}#Xf39)tn8H9{OKg|UQoMg!^`UgO zWLZIvKIk29(8oAin4sq^aESYV_1BYlfw8pn@&b5kT9oy2bCK8MVU$`v1?P8Q!Jcl2 z^u9Fe9(PlRJj17lbW!s8=xh?+=+pFO&E;)5;DL_mV;a6)7$re1x~82{g8xQ|Xsax9 z1avD=m7K!_(5jr?ZoQ~1vH5>dd3WxtYkJf)AEJqT`o{BZ@ze8qH<=v^#Z82mjxgc+_MklJy|S#KWwkK@bEi^r)RZIcqe}qi>{T}$?EMR zUpRw(+MJ}Uk0$p%`y*r;v@r(~8z|7(f2SKYeOgujeJN+`GHb85LOn8JJ)3W|-YF{_H)*v4FnYB(P2upl!^^ySaa=p-bh&?fOP&IrW7t zc26=kg;*imgbzXCoo65t;U8!MD}nn#k@cHn3w~liL^EJPmKbWwOMH*?td;l zAz~?aPM4D_D))b4e>0C>@@a3|k;#7=UqCq!TAjX157vE6NS6@SLK=`gIEChZRd!{} zFp!R-A2{E&x?B|{wdo`WeoS3o8DSf|O&%I|EUGl|wIJt3087=Lj*I0kzlNbHvBbj`Zck*vuusXpkahk5T zK41&4dDb`Q$4#5MiY(MkT(1(@sV{qd=~42wv(YpOG8YvaKL@6FVV9_VRWB}(DUeUA zW^TTozslLBw?3jhz7BvBnh29HeGMX31Fh%)_#xox@!M2wFvd;ev3H)hpOr6#w~dT1 z=`?>s!b)4p4&n_KdxAfK8OV&JCuQ6bcl^qr5)QN{fD}#^%U+C3rDjp@1UX;{21&<8i zHKKtrMb3tXF>-Xk@-YsJDL@|7U1Bf#}`T{m>lZklO}a{kRwCPR27(k-c~`Xo`<2dMcz-*+LNEmVK6 z7xOL%J_>s2H_b+}Z`eIGXXv8_kU~ouJIUBT?h%B|0N?+ff87M(8IbbS_>tr%DmD%=nZdL0MJUrQ7|nDA@_DtfU2dWIOQ%n|6Esw-5GD@j4~2XdF6fwagj^tS|F_rchm6jg zD`6`xLFdTDFP#J=fw0})Q!v6`2MGHRLI{K)eDCybAQRqgK(Tueis0jAtHu`3E{B4t zWYfzmij@tym390L9E0SPy)b_UPvNucJ_sV`GG1znh-2WErag3@Nz z==ic&!FYd^K;c;rqU?NSR?Q+%XrJy<#kCjadISpmz!9*5Twe!Y1*i*BeY{@XXyJ-~ zmZ-|~OUibhT)`=HMBFyWdO z3P+p^ppkKtD1>nEg+=e!)!oyNN3O2#+Tow>uLU3XcI2N0Q=fa!+5?FY{ME{pS1JJWx@LWG6o5vxO>Wd*2RU6$57;Nr6 z1Jd)Aog&Ed$>V=d;xRmzu!h)NbsPTjCUG&~bP@0J(%K;=?f<0k%p}Y@$X2+OFP=<^ zrN4@o3YJAId!OfO|Ga1S>bi%35B9-Y&P!7_{S^ty`RjLJn8+J#68{e>QV(D6b*ACD zIqwii`?|>%Vx65*kjkOGYY|@gfeD(vx{t-}w!BlLo)3Rj3Q78X_L^em5N4~t_pdhp z#(%0#sCxxHHBllmH1L>S4p|ok$DU0W7Cf|y(zIJ4OqWIp-R0z~ zL{-e-CA)^|@YJj%nf@nDDYG~MKYkIZHOr!28ce{COjxnbfkbfa90)SQiDTz_);2~e z25n(_IBS3IUzYKvASj(nQ2)ZYx`hG>dg50fZN|R0^aNcu*)dYSbi!?oUqX~@&Ehac{ zGeJb;R)MT@%sfnDqjgB{XOTUL>7bph&_JO&>-l6!@c&{sL|fLWIGyrjgF!GrIM{qb zuXBGa$|KQ3q)cK45Tk$hac0*Z+UUxr+^f@%Dz>;R}Z z$dJGNcu4&K_rIV<17tGF;7&LX75Y1q3x<=J;(gdHyjeFi^;O%MJ-x&0ZV$1=%W&}q zb;I}#5l>l9lGcB&lMS&fVimc&+2??Bt&i_9E{vQDU7XW{Bug-U> z9sYw_+YxlknAyzSe0pxqKRO!W>GjoFYey>NZ>iM3XQf?zMexr>T$j#oU_k72u@ z8HF;n+OMdns2xf_69WIj6XAc)+Apd}Qu>FYE=0QMJd~7_>XR6M4f{KuE@ao#)QIqL z|IIo*<@y^&M?8%IssD49Tf3l8(9nA59l#(UJQC&%7+H&_*`N&HXk!1?XV&Q-hvjx4 z74r|+#UP3}I^ssh#`^A#XA#lS4=L2-QS*h_T!)C6U{Fz9id!ORmJf1{H!;Y#F+A>z@MJftr!)5o}7Sy|F%%sH}7xTJW~H{ zDlG|YivKXxD?(ti@40r^tQP#PuFXPUQdvx6$=r%Ly65~K9UUE0_*%UzgFgRj9R9z@ zLDxMd{LgX3c-K5BOjv)gn*Ul^1*_WH(p?IuL?)N{=^1B~#3>{8H;DCrK%})U5C#5c zjEIem#mTot>skw--=`!b1p4^+fcsqgFB8TB4y+IlEdFOS^S(rs+fgw!rG&%f%tM`^ z^QMd`?daH$-@3Z8fx+iptDc1YTeTI${MV(Ys?4PSFkO5S2D5)Tit-;5z)48mXEKmF z$YfGBQf}6K6W0HAOXHJ%L1)RO2R-vMa^z$!IGAiUh#i4D>5)MqYC1B zv01PE`FmRHnZKdB&=DIW{6`RmCt%R+`ceds`}fC3qAtkGRuupP+yBI3kNf?%4*~Bn z{P90supahDloEgb80>$9{Qr^(j`VvK`!Hal{!bS?68-jLl7J*LiK@yY|3v0=$09W7 z4L9h_{rP$xpBE&H^*YE>*8S|Bxd+|#@xu@6jowimT=w1N5;{6zfqcw?oN^bV3(2R| zS$qQ(0CugCJ^x8|QAEb-MR|)Z*TR9L5toJgM%?6ug0p|+s3YyeKCWr%qSeW$Y8}pG zM+eTl<%ABc)1g&w&pnr%+^ioxk|kYbtC)FuW0b~ieG_1NZT4d8{pH=oXajs{W5GO@ zL`SCq|8&8+r(iv3`?TJ=-2~tC6Hw<@T1=d4{&+txmI28bOSnt)-%9&C+g@r|^txi? z9XEfaE=Yf*SOFBBEJ4OCHrif~@9eug1;|!;JPMp`ynV~?I$*{zcMe@A=69U8#VIb~ z=}o5WZoTyMIggFN9aYY;x(aaFDy)shn`^L0E``(8up(T!)?O(H9)-GzPSsf^>on<6 zbbPjr`++$rJaaBn>+nwQa+nG@hg(bsPAZX*SMq-ZwrrJ%+qx&;>;pHykxOWVwC=p) zR8DX#oN@5fK6BjQweohNWlhnAU!?UX*5Zkr!?Ugpc650Mci9>5AEM<9SAyp4&;4BA zc_hFk-s1R0Coyu@M?3QD`fD0B#4YBvSz&~8CIeZ&*9=_I*>H0xW)H@C?< zVDHeXLpi-IL11IYL?ogu(b+@?v?Eb3ctsH@vVSEN6{L(D$T@$~{ZJ?yr}fU4Wh!y9 z5-QF|3u-JMJXz0&NOUpqjhj?2(`Z7tRk45Q*6aLfcn1sK^J?HR0h$*7%O`C*nZ3#w z^(`<;AoiE)tU?QT18qOKPDSs*dw%SoPwb$&#mfP;N3{pF6E3svQWo!3DPQn&rbmD^ z8+sQG&lmBS5B`16vCK};P0NiCVAl}5Xr!PO--xHZTU3QWZk$?S0?I|T?L#Dpc!@AbQnTj$$%>6)*Ue&CxjCKGxI7YzV3F|yj5FS z?Q%{R_cBPt!~3HLuP@q_YB|45Etnc!@^m7gPNkhQE5ZAY6ZDz{Te(tEA2oK^rF5>2ww5AE#3L~ z7@N-lC;m=jCGHwlHDFvh2|S|D<`YD1?h$s0)E4F!`qX% z(cP%w>wV8H9^wWY?(QU3lx=UI?%NST*$XSa+ApRphp?ZDEh{`Z-M1_D2d9ga;F0bR z!f}=xC;Pl+?R@wNueWS=UK^?qc@O1-vCm;uE*gk14d#XQUdjfeHTkMF<*F5@02|$9 zB99iLN0Rq;<_yTMgjau42WXX*cz{cm3`osP5evZTE=P9=8{2=UX<0!?a+4blGN2M{ zmZE^UpN7L4g7?)5amvlBw{yBW0>n%M5kj%9n+` ze{$YbD}5j7eD?d)ot}o!WS~VBL-z`0v!7>R2k||#W{h_ysGNpybbD0jzfQ0^E`YyU z*bzEnv!jR=d?(S;edn^)?dIhR$4QU0NmrXG>uaSS6BECq(2Pd*x90VL#x^&*-cE&< zM@kGhMg|vBpwpuXCNoasUU1~qpOM%w z@7OxBrP2nC-LYB#D|Wl57PuI2$SZ_ZARF2{A%eecsT6-SKxMJg1`O?_%3!+k9mBb- z%X|~9m7l%N0J}rWSuQdJHL9-~8xkXZTE(Bam|X*V#w2`TuTRwD`a)xg$V2+ZSHT{TJMT`^k<_K=wkgk?|U^faZUPgC4+SL1`+OM<&d+D3dySuWKy2 z-ynOnFvAp{%8EEmDtUeO>YhS;0jDwHxFKV;QB!$;3ewH4@lxVCi1c=7FheJ8! zCWS1KK)ZS6_0@_`Duo4QU|jNQ@6fENv9W{Fvj1Iuy=a}tCQCSIT5-?q8gP5xbIQ|$ zaD;!*LS^sVod@rDrop^A;2rU(ZJ_a|l5Hzph0qj$} zm@s1!qdlnIV>}TJCsa$0(9IUN@NGQjsebrmH2T>c*_n$m?2Rz-Fct@x$^QQEREOMM zyz^pPj#(e>T6n?7;r5E@g3U+^wa#5$95j6|=)EuRmW71}ua35^WYx7U@{C=Myc&O) zaNw8L#!!q{jHcUnJR$}*JyR0Lm+Tw61|uDiz(Lh2b7wpm7*6y=9TXaIT>qN+mD*N>Wah$c zBt~O9CpSMlH_FiY=iaPEp*kd2w(5Vb8D}g{rdjns%hWT^iK#|a*fJXUp0d(i1T>UT z)%UM3IE*-$p<+4i>&dp$aX;It)S1#bFiRKWh;3g)AntUCGV(oo?31x&4K@UE8c22% z6Zd}7XHpjIx>)>byDOkxSBqL!krgYj5znkf{#+^1?nc@-uy@NQrN0oZUYUQ{Y1*L~ zgg(ol@0ePdf*;HeJ9J12J=7b&kT8tO2m!yW7t{S@)!iE_h8P=1H)Y0gTjxa?2}`rn zUbT=yGmct~;u$PKjNc0K0}Ij%r@R~LZ2)FYiJcro%>@^idKdoU37$U_Ih)VDK79Uc z#pW<$^@~K&UD@@4Cm)|Q?UjF6lW}ALa_u$pZrNgfkG8l;`K9A#U#AK^^8k(TYNp{< zc|Dubm5OfKC4Z|cGBL)z3Qq0Va#yR?#4jNT+rqWUX3(mPPo2ESm`;lD9nqvhFjt_A z-Z5Y({v}=a^j9k1)0dzgvF*m=5`^)IsR6f!#`=R()G9YyMnT~|=Fxv>BY+e`%8irh zq5vbs8QMhigt_pXisPcP6&@JFW%d2(Z8lwcawE&m^kJjfK{$ z**l;p8zL$=z_>lQI0H}%)PJ#PwhPycSv4G}2%#UWL5^cLBz|mCU8! zw8@i0%bgp_LlmBUvzC9B!L>a~ojJ+v!y-K4)x6-%=C)GWFI$Ui99I85{*c1wB-5U0 z-z`Bq$jA=9L!e?+UcWi5+9mS}9C)_<{&B*E>`O!WqbGo;p}4+dG13IMa*{~Y<0E9% z-q#QA&!%-nFl$;h@@7W*>M@GnaP0fj@w#XF1CqEw?px3HCH#M>!7XE%x)aK7f$_>i zJ1Bc^P)Z8T676g+$_VpV@HXO6qOQBAnL!7Zx%oCe5&gGwhx}HRozi)*BR(gz0;A%_ z=t$dy0ce)L#lfCCodLUDBAeI^XB$Z|Lbt`A@F4nVzIJisKjq$4${RPn7fFW6@!W&Bny8GRgS{ zx{nOQC&KGqm{jF-R==cIcjJv+TQX6G5kb<+F01zkgH3-Rav%}k>fgJ}QitHPTnOXR; zim*MSfmcV8r(Iq#oln06=j2z(M6+qqbCg9HiRZLzhUV1#Mr#8(hl z&q0-S1#YENeH#{>-ZciUE~{=~jd!D@b7fY3)2^9aTNyI_$9qh!Sl)Rckr+)Elf@*- zDzAS#m=2CikL9C`Q;DNFsAai{tZ_WMT!m1?5Fcs39T+4|aP{mA0Z(=+`yywfamo4G z&AKEZI_TnQ9>jXfSIYKw^wzIe1M}0p^#f!dAc9%Unf!ofQSSQz&c+VYn>OXu*sCV( zp{XOZOGJIdr=~Y{rmZ$}27B+-Z;R&G_fmh2{hpWVd(4f8c9Y7Be1zwo`1Sh}o|>n8 zcG9z&?M^EZ!K*+iA3f)nZoYr6 zS?7SQ^F}se#Xfp^SqG;|lQZ}I6$S2_IlLb#Y?#6zbdO4Fk7JyL zZK7KbtDQpOw}!69`@ES7g&nBWB# zx2(k2eYc~k!E=KVuw`25xj13n?s$LQ=%$_I2}DehXh!yGJ*O02e}cH+r53%dzu$Mi zD%m%2Iar8!kh6TPpy~ST z2{^)l)Q9AQmgVszP9YtHYIEJJthSw7cR3iOJ#5S_#;d$^L1;D_#pPiB-kN_A?*VO0 z;@)t&KejtBG(N0`E<{Hwx}P{^$nia*AygWQw%3&m6Zx^es>mp zFKD3WpqfS?=IhwYtQ-su{`rO0gL(B{^5#}MVE1Zoc4Gf-CqBsS8_-Fi%zURwd<_C- zxms{h@4Se6RX#r~vxQSYYZQMW_iK z3XKp_u5ItQJ%b{R!`b9K2=h+AeaOR2o_6RQ1#2Z838vqka9>t+lgmN#O>!vXv6*%` z)0_2$S9N12gbp7@LB>P3q5al~)mLNKsQ1p>Q{|{U+AEj(%nj%A)E>lVyUdQh8A3)pZMHI5d~;1( z(hRBX#1sd(Ug0D~@~?kGs##8^^uZ-disGL;FxTTby=;z6c`~q2DwVmW?8?paFqnHQ zVpH`FfJ6kJll!WV3s{UeYOR;oP~ekQzcufM9w!l&1gk05wKXo+O;Md8nfEd->O_PU zN()4{J(Vu()51TzHG6TbWT&xLaiP1;IJNDM@@SfzVLz6OhUkA#6{uxO+RSt;Dzul5 ziQ^8z%k6~gKdVJNIXoLxX|>TZN!1A>GwRXxU8v&*!daMcS-W#7s@d3q%8+Yn?$*8uBX%4ROl)s%bgXDkW{eRTxfJ2wZTs0~J85yEA4W*U zm}l`=Et(hR!m@wgOf_R)I9?oOS`E7$9_$TIX!Nzixbo7o|7=H(JGc;Szmh?y%rGjw zQ_oyb9cTZc4+!R}BF0Z>VyVX2xXiZ>bkQzNui3AmT(q%%gizdf;=W&jA_={6Tpyx% zrR|B)xm*ODbO34BbWdCLu-CD!a8G=5yor3}d7X<@1WtcqJKS$d{Mg8!1l5l}Z@+Z) zBD0xOf?oVK*>S%7b?$z1dLn&fQ}c0S=^6v_jt*E_8g{ASAd#*ok!XsfN1%J+&_;I> z8;{@a^VJ-&W5jmc8n3s__4aD5*Q=|YFNLDhG=PBQ|D)zFAL0tSEpa#@Xh?7g?vS9t zT@u`##@&D2t)YS7o&@(0TpM?HcM0z9+E~Mn+_`h-ohQs6@P4T;r%s(+yVhQNRrRjs zQi9e5&JRdU&7X*lFtg44p1BpVp^750Gw`E4O1XfRAV{>`sVo^sdmbvyNho-#j^xV-=h82fiwXN5tWsUq!Heo|3_59(xkTE1(*G zvH89)>SepF?(X60wgIqh^7KM%GRE}aq?!xDi}xqfTc;v2cn%GO2N3Cyw9J-08=(wYD1 zw9jc&rcM7Q+N68629$nVaesd@bV9^prg_-6O{VvQLVg0GS?IRVr1%6}qv|h3{MK$! z=iy9g>B?lmYnK!(=|+w#&tEBBif$T^-A}0V10B(h;v@rA)n;hApaqkA|7&V(V?Bd8 z^k(}}y$Y;Lx%0Kj*p8xbDb=p38mzUTJ=)qZ6*&hd*n`TH9(crJ_w#>BlN=CJg?H0^ zTOC!Ca#-ieQR{clB;h>lMRQa?TVL(dKs`fV+c+{)+HD&CZu%O~G))7Gt7qGY!lZB( zpnn(Vl>IcrGwpE!m)DDpXaSZZB0Z@Nr9(sbkSr?$1-jl{Bv&iMh zr>@Z-;v2;pIcDiW2&jKWgUznZt#4Nr?lzzs&3c%86LG+R`|6#MOKtHKaK-G!d&Q`R z*1n|y8LE{}Bhd5`yETD*kHxc9idkEBN&Z`@OOtm-2ZNA}oCnfGDe0}>E;x^$a2Vn29^jn-3tg`UUoAdD5PneLhzy))qZ=Kf zE-@!lAsgO>)ZLRz0trKT4t&ky!@Q;0ikVq(|?kDqkC4}9H! zAfPgpZc6ZP`=~}}m6Ggw&a6fYSW4u16_>(10oRe+&Ukg>jD~-QXco`h9w-|ni<2IG(_)T0 zF=tW5_x5Sd?q>;3gA+hmP_Cn7X7r=S`?vJ_xF$@cj}pvbxS*ie1CS*zL9mn9-s%26 zzb`#y?ENrKfSGAWl{b~E0Z-7(QLkzJ>?0d1Ay5GHPM&^9*uLzwiwiC2P43bYSLt5m zc%`*>{6>G$`*V)!FLh6DuZ}_cUm_6ChQ-NOd=+Q{bc zf$Z5c-CIw%^7P0(va``a94^&^{o83ehC0o)6-f>kcn3281l^v?>>_m1czu)|lF+lH ziBigbH1P`F@IXoifkjJ1My%5Cz$|crjf<5fNC%{ zsfTsVy_wybQ&{_h^6{qzu)z>Wviw*Qp zT^u%;@(Ar@KC9u+O}|*e=$w&W4a}2BnRc{8wt9mM2$K8cQN_zm(^Rwt$3LX|`g}@- zX-0C`W|DZhnNicOa;9c3zQ!(kWpAN?sL+3Xil9IfZxq;)+TpUc-G7%3Y@R*5eJAft z>-b zNq9AZle%%WJzeizO`~Hsq@ryRE4RS{zN@Y!J5qtRj;j#m+umtSVk6TQFdB(yh=S5*jtYz0T)(Q zz~Q5wPXc)jc5=~Llw-U|htNkouBLydG&N*F!->j}(At)VPDFTYY0w0qQb zV$!!(1|;r$uYsOF2(5hwUW|X#8;WtaG2w?qS(Tp!J{cw=MsR+-u;H9lR}G!!)ZKKQ za^dunpmuz`{k@@|wz<~gp_s@C!6;|DV~2;{qO0)T@ut>6sTK{r;N6;WsDzIh+fy7F zkJ}UMw_c%>?JNiRh(E}6)y3jykfZ|zThcDw785oo$+?xVt(t?xhID_+2KGXBOx2vZ zbd%@#e&*O7gxA>}n+Es(NZ|KvG)0Yvetp=e2}XB;@GWjbV2+{N^aRSmfZiT)4|&76 zwV8#hBZ~Cy(_N&R&(-c*rT1ma#@Ol%Yjbr^Om6!5YNO!w0h)|nEZM`tHslF-v5&8( z!f&o-A_){5M(5H;e^7tf2PtjwmrV{ z$(=Y^-YZPtvEz33oU6=GqmFkGt9(GccK5Tv@jq-SS)swMBwU`BlC#ODt$nD8E!}Zo zItypnW*mTntJllylFzql(cZY`JNc-DI%m%yuf=*4m=}_7U>ko9Q-!4!ZsBR+w%@(r zQc4w9Q=7h0?*=0D_6E%H?T64 zml|{X`7w6AVeumw-mhAkj{niG9I+HICUV?n1Qmn_Q@wwgw9dl#YOLg>ljd@hDw!+2 z>vo2%wkF-Q1zv!GuF?_4e@&`1?a-tD88yhQZ1hTr+RUnx#w5t$7NtjHW21Tsp>JSq z8|-NEM}zRZ=t1Fe$?E0+o?8__ugn+X9k+|(U>p6QO4FnzVSzsXsH##p#LJ6!mAMcd zc(EhBkKuo9gJnC^R6gggHp8ZEfZ1ZKQHFEDn#&!F<;5yx!nH+3?)3>bm773A-5*NT zHWu*dW^g#=42a&+G14+ybehw_mY%PqSJKc^=$LTPE}TpH=K;OuP$N<&{!1MT8P3(l z*R#=XtM3G5xPEhjxC>0ooLzpPEda*HED=WI?hc6aEyKWaGg@bGj4b-u#n+sAph$cU_%a6^=GV`|fTy5d^yPYjP8eTot`I_|I&lOXtko)WJ{OjJnF zT%}PPG1Vt>Gt3Cf$$$N!6#D>-rU3x;Bm1&GmxW0lJdXH14wCNg{;NOdA! zPr!fa;hD&fTjiS`M5>zD1ac?2Vhg_7(dHtGhjp>Yqwq@?^0lequg2oC$6!p}*r8^t zp@Ap08Mv)m^k5&ohRZuimm&R(moUsl0&TW3;jcFyY6QSEwRm=S_*1TC!T#RpUH_8_ zR}#-y7+vXQgvMNm(kN2gFM089n}jek^y7bsSuuW+pdn_-w`d+*3|zk+a=>^ew~Po$ zEd*mrmAoQY!J+WzhhP&iu84Y-jn5MrWgU_p?Z|7W|2PfDt6r0lmH5S7MMc}ZF7PA$ z!)+!(33;k(4GdkX*DXS?C346dYIQ6LE;l8mQd2?)KaG^LQfZY!P+(Y*YNxQ4JsE!# zfs~XQ<-JL-<|$eDe1vl;>lqFo7B_nD=`{heL0{ASod~B3%fsUx`tn!Io+U7?azOO%HqaB}F`E&0hFO_sILoo!sdGp|t@n1QjAjSMVo+j|xUnZL$>(dY0?SwoMW+PHRIr27+1>%3QpfSpb zf!*2t&i?*&WP;DIN#}H=I!u)MAtI*R`Whj>aO0Z+I7zd!x*A9H&R{Au0ztgHS8ivS zc;YIHBCzn?>?EHfy0DXY=)EuP)HPJh_!%)L698pEn!gBfW9YUPsU>QOs~+8O{O)pd z+wD%w{uIkJciBL1oY=&&xk08**H%}5YmYu?EJi71CQIn=B&3Cty}pTlaWu!WT{cUd zojsDcC|#BhM$YZwBvH_^yh+)k#QZL;ab!qnj90+1m1l=<=(2EjhEraO0cf~lwN8NR znX(ME+}*~MwzR+RY;8X!dTKY4*1}P|8(36k;7-dR_8zToJ90*(HWAM%tU~dBH({3> zrjqScD9rfk3Tp*+=QKdBM0tM`c%#krU0O%(;T2L6NZ;-%XoS)yX6a58GtkcCbnSW)yo{oUZ z4oR;QbSo}my2V8@Q#08Ux?AnVnfv@ijRr@~=*=1Q>}-C2PzcTuk}Bk~cW?uRFK=R< zZqFX=Au%Q{j340Gd?-M4U-u~RM_hc&hBPhn@L{X6Je23eesx%gxopym-7(h?t%VC)A|MAx-qdykO&bm5U|G6~%Dj&w48;Ii|o*df>{H$cct1ffwLO0zqyy{$_=#aBtwXBz`i9+ zum1vLLn(At6!~j6@`c*vCX6w)RqfEwgeOEi^vFNu55mPOf`mRlvDq-2;a9)>t?&H% zHk}14!wa`NC>0ujR4tm#Z!8B&{#2cDoir)3oM01wA$=EeBVFYoY~tMv@p!4=n^=sL zV}o9shJ@#DQL(sh5x;9Ik3rPS)z!YRJRz^T!6-2O=Krq_4J3cBpn&BPDPAt{CiO!h zf1ZWE5DWhQtHZMu>i|d*=61--pOEGz1B|5t~5^j|mA(ER{z@)tkx8e^9f zCI}^eEO`9o^3uN*rg`Z3B_-Ya(?tg5>c1xUK6{)S!y+JLur)7JfTm-~pX`&6(S*(U z;4j0Sxo47ifjff1PwXRY;L;BhN&U`QbyX0DUHpj~JYwcU3 z{Z<)Tt&>i69yV!6&WDW_^yI{@L2qfj9JSbSytqj;uMqmU-`A+SQ1{z@m`4$MQlfv za&oH4?Qz?wS>JpBgMe6gxeNBfu5}lG1t`n5yr__XXfPBtT@xh2TU^*oyz=|0Z_uH4 z=zh(cYWH9jluN4&)j~Erf=&i9sX64!I?Rrmb z;eV()f+YDuNF9Gyzz4M{z!ufIM;mcyU&Va{GqkpBZYMan^2TAU>(aVc@p46UMM zu#VNtZ{~Y1HZ8eN%yNO~t~u;VfJ`O8aS}_W+X#voXZaVL2?OWgZ--bwbSewCpq4Y-K@W#!VzVRS3Yq?I^h z*c%4{BSukKA;6M*q9G@|Ag<2$n<#iAt|TYQc`9gH>B!64-G~^6*vc^#xxWs!wYAIp zdYY%@Xn=OJVH6)yCE7#Uxpt$cWpFZ6ET;;!5x<7t|2RjLMv8mX3S zkA72-A&vMRs2Kf)0|Ip&3Ms?ECX!qx4pE7tJWf;kZBa+~DO zc6&`9u4e@U8sDpbT3O&-;%tH-@WcLCS^aW0y=uACXE4wb%QljEo^B_l&xJan{h&R zPW}W%XlA3BX?qal=4u85Yg9&lO@$_I8Ji@tkrMx*T`$ndh{3O#&7=@se2MK{(em^2 z+x*@j*h7}wjCRK|`}+GWoOYjC7OyKvw3Y-po*tbHA)hsA-0T!KLNZb)OA=K_mCQ0Y zJ$xlK#g7z!-Tdt~(;1gq>w%H%%|JScy_7H|I}}mgyB^lASiXCBqnLw5T;yh;Wz}*B zk1%p|EWSj0-_3i*Q9CPoT^PM(LiAUI25ey%!^uWD7)oE5f4v3By~M;EikK+KnG@!S>zq zpeW`ahPNime)5Fem2XL}RNL#WX=+JX&tV0CS1!dkYgWaGk_je*5!&SB&E-eDgg9qK z86g&bQor_i9}E|nY2Hlpwzg$u_(YCY2Ce^AF-f1QP>$3zW07hO+x#s+E^vzgA6g!x z8hK`ZS(0nQx*d}V1Zca_^ju=bU#zrg>G^~h_@A`@>X2t&vsxDwY!~e`%znqT9tmFQ z>?`K|ZW+EUONkVZHHccT2o~IFRPo@GyS+r{0hb?R%(G#i(fa&l2?} zM-pmn7704{tSY^s|#o1{mYUk+aiSZY{ky^n3 zaV-8FUpbZUX}oR*)7RS8IIQ(9ykRy;oP*!{j2FGiWdaz5-<1es^G9#_WH8MDgdGYH z^#(*uhTBu?s1N5%(xhef= zVI>I*6;BkY+8rD%&nkO6UeNg+|9}x*b>qHC3>Jk45Wr+ojTvCTYAS@cbNG?R4_%oh z)9O!@O&qN_wt7po=7XGw*RX(f%xwt<#)e|K8}2pXG#NwBw}A&f^j_!Ws42~V+jpKt z^XOQVF$+L~p1hlPe8@?(tw91G@JL7(5sactznBQ^f9GNl++sHrPk3tXAuVquzEv{o zednE-fAYsc!KA^(3^AbInECNxQ~Bnia&&FfDWmgXLH^?;HTc-rqQLV#A)9Id6Tu+z zDEc5X1;-X3(;SdK#EugR3Q{_MoU6x;2Zequ6yqYGDj|KB@%inSoLk3IA%g3VmZ|Z< zF%=yk`WTNvj3AVm2UG&Wh~y=f*fB+zn4Xbk987BMZ)b*?H8#K&_D2xKX90F~quN&5 zcivNqP+0O%l=Tyr1SXe&oDD@77>KR#nwyYyeZ746o+JbV9qMR}}uMn@9pK9+~+}SsX#&?{_I|)|2=mlE*Af^@BKaus;Lp z>_1gjY+3?u4+|Q+q;Ix;d&JxaICGom%l2>}U2Vhg1C`2MOSM*}csg z_8-vvY)gsoNwJc+(_AdyZ`7j&FQFPu9#h77IyHq(A{cdl2MvGq%f1J>n$HGjKjyFd z9}8}@qUhdcDMQOS0DPhDF^(hFi)>_*%&P7R=jw~jv+BPbQI~zLSiK(REF)$VV&dxqF1UO?|ZqEzPKCQ;}@4Q%oNXwL1H=$9dgd&yyu$hEXx`d^{ZV= zug(jQhPy3)U?N;5Z(GB7>6ZrvZtG@kVnX5!Go3e}kdGSWI&wKi-;q}RO<3HRTO7Hl zusmE!a#c&LaW><|N2@WrUWyb)h-M^1O4o|mCErF=j+)wcLSi^%I$40drHIO>sl zOx*VX9LImI&HnxW15en(qM{$^>80LsG}LZ^8_w(dj|ML6eN6OU6+tam%DX(75B+aH zyDut#>^cL_mND?)KmP7*e^0Uz&O$U1OvR2cO|5IUY-3S$RVUNOgyTR-4(~tNy-rt# zehWm^%3>kvXsFh*mr>h)-fm$`FKYI9HpUuCGrr4W?bKhaMH{>hQ%x>_NfEpy6#Xsr zUcOrMf>JigcQx0`>jxmZUAad+uzHNGoU2lQfADE}B1okWn<`v{bU`sc=yXn-ZVTD| zN~-36{}+H1G5zmfc(=FR`9&6AEG?;;_>r!N*xUn#hE2Otz@R|#PgZHgn)OCUnuNAw znmc7jO9C321HT87w)Rn5zNE>_2=eg5_*dr0vK@6GBi9UsMOkijL;qB{0-d~fhq%ap zY;t&f-Y+lF5*`T)YZ2B(k|w6bF4OVjhr+eEd?*1t~UO?K0i86f-w;ptd=) zna1gUEYj~s_(QqAy_!Dc_^?4kA}qry$mC!d=OF)OxDon{;G z)fE{-3OOBI{UTA$hXf-IHTVE1=nz*?Hn{ob=4q}Gv+d*l{nHcp`}=y*wU%gqAw+Q@ z6vb09SoGJUmIu?8)zg}GKGiEVcQ#K)kR8YZsL1C)5$vhoEGk;Bf`$sK2+JMt>h)Jd z2=>qK0ZYc6j{>YIOV&THg+fW3u(-eNZyY|W>a z?_~b7>|lDzB+#D6S$1qEGoICU0?q1=Y8W@o2;CU6G|rwK0lmc}O*sXd^#fuWJDIAU zPnDPt&^of2KgSMuv}6M={5h@T*8i@jGGWN1Xeb5f>oH*Hk&%eLW4YN|=q^TFat zk}$)o03^87%DV^v{MjJr|iLabDKW~`LHK{1i2qW*|Hop2+W{h z-3384)D`Ye>Zh5bx?@Ikm@;L@y{4N{fbJYfooIK!@(qrRZq2&OL1JF^Jkre|%gt=q z5?pS)gwugO`xaoxTo)HNQPG0B4BJHh>(=*Q@hQdUhc#Wi)l_;XPf!h$=w=Z?{CUwM zPnS|G#9~<&)mhbldBBV?IxV(kDzcQ!>Ik8mjQP#*3P6!@@_KAI=k~y|+F>TkbJ8vJ z@qDAbrj~P}VYu>Z*5XWv5Fqdxb^>-J)BgU=9xVvfEfst6R*z(p@`DhvZ;YevSQMKLjcV>rW zsf^$z$y0uR^zwc@KF~BsC$(fyB+I%;RjT}FErZbVh1IjklD{Nn!bsw37b~LG4=NG~;<+jLO=JPhF=9+Yq=6H6Ct__#R2+kIC2Lvd%|MzgeJdo@cj4t+3 zHU0lD&-|}OAsVLSz#{qzQC#t#XV1;Bp6@E);)FtfGvuXr#b1BZa|xgz`1g^o&;>5( z&Gf^42oCwbqasczJ`YQ@))(}`sACUsh#>YSf!~2I8RUXRdZQ}NRYCT`sBAvZ_6)a1 zo0Gj{5D}cV%aAw+f?q_$yWoYwSyXTqe`eW#-b>tg!B}rHza4iY6p8BV&#zn>U`Dt)ujK&gF^mq)&0PY zvya@?M9>Y-gXqpG1g_@G}^>r+C#f!G-@}*nNrxdWW_0G7y zOKDdP{&gPfw1YNel|xN4SPK_k4v(coZ=MvbM(SG7$9c6BSj? zJZU0OzGY_lwmu9co}}P#m|-Gl#XAT637F24I#Y7ajEhsKph!K2qiUg-IfrWkW8co- zO>N_#G2gMMjh)gQ8tM>tj+4^Vt?1zRu|`RggRSY=h9J&d`2QQJ&kZ6)nbj;~iB|1@ z$T9&D5d(O(=;u!iY;0_cgPD?wZwgsrk%cU;YVMm$;$Zar_M>o#0Nc`n=9d;RFe!&8 zR%VaE>AL9@bf1+LAD#Ix`5?D$TtnxWlso~wkIh?C4$ExYn}C;iQTM@_tT#Htu*nG zABrkW4z1rk!#FZihUnjblWW6Ys&?4uS*^*>|0pUZRwx{dg3oE2`t^-|quYsEhJg1} zz0;2V-B{du>hH0HxEXIM0gsD+@Ney4oxO#e5P#3D*2h7)Mka7%*v#?Io|J?ZzlI97 zQy*T4oC z-gnV8X!R{>!*!omlYkF=EP_0-lQ|OGw6wpX#vCDtcDt%2IKqh(IE@BOFqlSx10*Jo zD_~7}^N} zOo9)VeS&B?Ef{_dZ=SgC{j?9MoH{4C*9JE-&1ca(ItM@MH0-4ElFZ5@B;opzs!K$T zt@it7JbY2EebPl;su_|)Ph!(}a+3Y2F^bgRZs~*(GBynXa4{q+UliJgpQMQw$tkP= zk!N?;%qpEnp$W-Wv|8_njr8;3X0As@?iOP;{TIJkg?p6T_E#)3qJZ5qiAd3_QkJC3ai+af0h6NU7j`ST8~j2avr&N?bh-<(#i zRcmYTMo-OqCIB+caMn*`3@y6F*kT(iGMy{&=&+3csUl0i%GXmCN-*tJbdp&vi9X+W z$2zR#t&kRf{7%(oS!ozjXO8!u+#AXC*>vawrq?-Y8js^KVzUa-!&aOD@NTDXwx|A8Tvs1| zPalt?t6D!Ul#7g>88+8JiPAVsjpsm^eV(gPJ1XYC$#ua#7iC3H`9K;-JPOk^$>V7u zN0Iw?iTHV zpVaU#AF015X>gS&Mq#>5c|2V<#FlGOyLh|{(s4m+>EslJDk6Xr~)z;_2NRcfvrA4AS_)w1Spq_cz1Ma<{`) zhRJ@gDw^4>o{mdMY)h!_ikhPGr?~V8!b6yuoL4ZOP|>c4wLKrI%XT(SLh{m(zPru} z>wEnAbfveF45%Mq&1#)Vw!svCudRh#3iPJKywicMU+ zcKvzHyK_#7)EfGsZ7CI6A zAicO-TJw*u@e#NlwQ1 zhaSzgxa~i{GpB~}U%wx`%WI)o{`C>_#!<5auBEtYrb#yrr*hc9D#zH`oa^=WgC&=i zl-R$tf@W^`ECpqM7hkJHzqC=Zhk+08;{P8bShZ6{vZ`mt5~O%A?)xP)A^qJ$H*N$C3---}RJ zJVlG%S}MMhe9;=a4H=cYNG04Qy#v6faDUPgV5zJ)ERb66!V#cy>l^|eDSZbd$AZEvS>xKeXQ&p zEjZqrVQP&t6aeH8KVMk2Vd-Dkf%d=Sy!I)fEDipy*MvpmX~_WbG5miK@hrb^%|+Xp z1a*ynoio)&(hHcrQs17(cUkQZ1FS@3pl_7PU&`~7NL3Z9vjW5Ejk@=A9=Lg$AUuG7wRQ6)}yI$g6;-vF)1bo6+G4wmTtykAtGo>hhXuOIU(g$~|=y4_Um zgsZbY0vzkHj2_EhIAB0bZGtJGznc`*S@#-$n6H(K7OY{+24t~AXHwW3IOM(F^$*j& zc;10!Zqg1Wk5kWTCO>+T24;&;QH>8*x|w6Y(7O%k?`#37ri+e6rm1LnNiV@(GfapM z8H8=QmU2Q@GA472Jt3@X_bqqXy&#BX&pYI8(F;}#;vz=Au71cWXh%0b;Vt|owMV{x zSyC1G{-vW72AyAVaINdkw4BV7Az@Qs{y(ojFMupD%uZ#S0am-DG(}z;^YL`j}<<*-N zP6TelIIh#PpB~OuF&QIaUN8q1U+nL+-~9$An6};*OrQ`~iY`!JPId!ir$f5>@R9RI zedJSQ_Y;%N3LNGrMP6Xkd_g}o*M%(caNP{dcU~G1q8=J|r`@R4MM1e#Ng0-ZGuv_v zmQp_b%1WKul)^7D@^eVDpAS~bT3wJ&lUo3mcWHS;x4|XTgeoR`{ z21)cnyur5zC{t6(&M)C0l3pQy<&QiCXUXfraQTU`I;Lul13KtYIe38f)J1V;w<2L} z#xqB5tSeoV#}Y$ir#g;_Q;r|Qu#?J+_oYa{M6N1bO;~5!2(sYeI8e?Z`BbsD(B(4Q z4baQY#+PvZ!1mQF&NY$7Ac4a@*wR|*p8EJ}q4*1aLZngf|1Muw%wPq79GR)!Mv8>8 zgA{ypyeApjdv*?&z%UmXv1p5GF>$+O3HyFz?2?i%(orskIh7O0<7$a>@N#bvQr)AI z?m38iVzEtUXBCP4WccNUPjeN(YD%6KNz~7wpLe5-DIB0jlS%IvK!tV7!(K?m_AE$M zKUV=32_A1%4OSDbPx!2VPw2UmJmZXB8oUfnvp-eO0e>+slH;Urf|Y4xydV!%jpV|) zcPV9duhC{3Bz|BKeU^&;(sI$H2+<*dBq^BEN~`5ZRn?YlOSG*Y122t7naw_a+GKx$ z7M@=U7Aisl^-qfnHs71tXw`l_U~$^ImSR35avs1lvs5tL>>p2mA%uHrIl2Glp9)l7 z&3=ke_NM{7Q8_1Stpetd3~D&^RK#H062Nb`k#;X6SF^Ev< zviF_M5doJZs?4DVBc#pm&0uqlE*e^qNXNfNzCypopZ7S^VLVzSe2|(N2!|hd5Pp`H zCiF$|_V*VAh9P7Q`e}I9=>%7=%wQ5N>m5S2BWkFzgf)W{*{7FKXM}|IP#Ea^a4LPZ zda7wm`eLVln736H6Y~XxFW}~tA3PT6&9rYFR^&_4x#}4l$uk-AFIuedZ1J0QCAJr3 z%3HJtOHCikm-xzD=oydiL!`%Ff}<>*&}oqcw1DU(5&in_PadR938XPI>dr zBsrA4@~Bk39@QW%5QEgqa58%PcPmpxQPOpdd$E-BsAJ^P22c|c6BC;Y|6XmsApEoJ zf7pb7&B_nO=YxvL{f$SS6tU}n#nuNuzc&c$&#-)Em%{hJP!%P^mOtSDy!*xTpa;U; zOR*Xdn)AGFilroh?yHqk4w_ElJBabFXjEo3@~MC=k=ZM#*ZDQ8H}L$r#KrjXq<^W^ zFUyJ%YF+IQ_ngI6YkD>NdTCIVx-wPgH`$Yaq-rdRXq2*I7N&p0z9iZa*>Yb|v|45n z`Gl1c#pH5kc=e1-TXeBSMuKHuXTLyAXy!7{Yg;`ub&cS@(GQ|UdwE?daKFxer0v@*Jq`)yiK2Lg-dJ-Gu_XkKQls#x2;`vNAvcwt%?XAvw2nl%$3w?1{jZ zp-~s}l(@D7MRU5Z7iGF%+Q#RKgmZmRu~pPA8|ds}8ppB)e-j2@s@W`mquBQ9FB+pE zzF!Ou1Sk za6x-zb_3DVDV&SG9 zd~3-&a62bdz!gnw8RKpI{$|l~?&ef7TQH>vMlm-j%12jx+ zTg)$nlNM&RI2RoXe##&KT^YwIPHYu2K5_2$tf@7scOVh!a<2R_`!vCo@L?xga(4gj z2hrvf4Ur64R820^vKOd-l$%5bA)azqyjCwObL*I3)Kx&Qu)mgOh#RfcW-aQ+(a($f zKY7z9qf_6A1TK~@X?aY-j#GYEJ8|n!yqQ(GKr_DmUk5F^w|lP%!T0sako5!26kL;m zE86xc2Y?09Bk0WXwm9j|I$^YF)_c48c;b(h?bKBI+Jcx*5N6+hO!3aLnrG&qw)8Ml zhYiARjh?J3AkgD!`pomlISti~2&pFaz~%dITRNKMT8oUPthv2gvgyX5QPbATCu_Ba z>Am>@GY3SCH>E*9RPL+LNi&D`?e4qYZ|@odf6RzGgvL6rjk-WX#!!uRtiHI+y6%jw z+ZqT8;(ssb#`XSxozcziLBjiplFP<_|B%3A3>u3%jSYBMBqn_i(I7nDfeHz

enN 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 rMhdBp2Y;_IfVUy-;0tz-oe5Ar_%ze(%% delta 475954 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

21P6zp4064 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

{lBmtSkiAXw(i`)Xl?y$TsTkU^kfYjip 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�Fi79%!UP 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 Date: Wed, 2 Dec 2020 18:44:17 -0500 Subject: [PATCH 26/40] [Maps] Always initialize routes on server-startup (#84806) --- x-pack/plugins/maps/server/plugin.ts | 24 +++++++---------- x-pack/plugins/maps/server/routes.js | 40 ++++++++++++++++------------ 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/x-pack/plugins/maps/server/plugin.ts b/x-pack/plugins/maps/server/plugin.ts index a79e5353048c8..f3241b79759a1 100644 --- a/x-pack/plugins/maps/server/plugin.ts +++ b/x-pack/plugins/maps/server/plugin.ts @@ -147,27 +147,23 @@ export class MapsPlugin implements Plugin { return; } - let routesInitialized = false; let isEnterprisePlus = false; + let lastLicenseId: string | undefined; const emsSettings = new EMSSettings(mapsLegacyConfig, () => isEnterprisePlus); licensing.license$.subscribe((license: ILicense) => { - const basic = license.check(APP_ID, 'basic'); - const enterprise = license.check(APP_ID, 'enterprise'); isEnterprisePlus = enterprise.state === 'valid'; - - if (basic.state === 'valid' && !routesInitialized) { - routesInitialized = true; - initRoutes( - core.http.createRouter(), - license.uid, - emsSettings, - this.kibanaVersion, - this._logger - ); - } + lastLicenseId = license.uid; }); + initRoutes( + core.http.createRouter(), + () => lastLicenseId, + emsSettings, + this.kibanaVersion, + this._logger + ); + this._initHomeData(home, core.http.basePath.prepend, emsSettings); features.registerKibanaFeature({ diff --git a/x-pack/plugins/maps/server/routes.js b/x-pack/plugins/maps/server/routes.js index 49d646f9a4e6d..d98259540f5e4 100644 --- a/x-pack/plugins/maps/server/routes.js +++ b/x-pack/plugins/maps/server/routes.js @@ -52,26 +52,32 @@ const EMPTY_EMS_CLIENT = { addQueryParams() {}, }; -export function initRoutes(router, licenseUid, emsSettings, kbnVersion, logger) { +export function initRoutes(router, getLicenseId, emsSettings, kbnVersion, logger) { let emsClient; - - if (emsSettings.isIncludeElasticMapsService()) { - emsClient = new EMSClient({ - language: i18n.getLocale(), - appVersion: kbnVersion, - appName: EMS_APP_NAME, - fileApiUrl: emsSettings.getEMSFileApiUrl(), - tileApiUrl: emsSettings.getEMSTileApiUrl(), - landingPageUrl: emsSettings.getEMSLandingPageUrl(), - fetchFunction: fetch, - }); - emsClient.addQueryParams({ license: licenseUid }); - } else { - emsClient = EMPTY_EMS_CLIENT; - } + let lastLicenseId; function getEMSClient() { - return emsSettings.isEMSEnabled() ? emsClient : EMPTY_EMS_CLIENT; + const currentLicenseId = getLicenseId(); + if (emsClient && emsSettings.isEMSEnabled() && lastLicenseId === currentLicenseId) { + return emsClient; + } + + lastLicenseId = currentLicenseId; + if (emsSettings.isIncludeElasticMapsService()) { + emsClient = new EMSClient({ + language: i18n.getLocale(), + appVersion: kbnVersion, + appName: EMS_APP_NAME, + fileApiUrl: emsSettings.getEMSFileApiUrl(), + tileApiUrl: emsSettings.getEMSTileApiUrl(), + landingPageUrl: emsSettings.getEMSLandingPageUrl(), + fetchFunction: fetch, + }); + emsClient.addQueryParams({ license: currentLicenseId }); + return emsClient; + } else { + return EMPTY_EMS_CLIENT; + } } router.get( From 401047e9b13aa2360211a24b893b47eb284b7b3c Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Wed, 2 Dec 2020 16:55:58 -0800 Subject: [PATCH 27/40] [APM] Removes react-sticky dependency in favor of using CSS (#84589) Closes #84521 Signed-off-by: Tyler Smalley --- package.json | 2 - .../WaterfallContainer/Waterfall/index.tsx | 5 +- .../shared/charts/Timeline/Timeline.test.tsx | 14 +-- .../shared/charts/Timeline/TimelineAxis.tsx | 95 +++++++++---------- .../__snapshots__/Timeline.test.tsx.snap | 19 ++-- yarn.lock | 17 +--- 6 files changed, 55 insertions(+), 97 deletions(-) diff --git a/package.json b/package.json index 77368f5caa7ee..e50b8516d9c29 100644 --- a/package.json +++ b/package.json @@ -525,7 +525,6 @@ "@types/react-resize-detector": "^4.0.1", "@types/react-router": "^5.1.7", "@types/react-router-dom": "^5.1.5", - "@types/react-sticky": "^6.0.3", "@types/react-test-renderer": "^16.9.1", "@types/react-virtualized": "^9.18.7", "@types/read-pkg": "^4.0.0", @@ -782,7 +781,6 @@ "react-router-redux": "^4.0.8", "react-shortcuts": "^2.0.0", "react-sizeme": "^2.3.6", - "react-sticky": "^6.0.3", "react-syntax-highlighter": "^5.7.0", "react-test-renderer": "^16.12.0", "react-tiny-virtual-list": "^2.2.0", diff --git a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/index.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/index.tsx index 3bf4807877428..2806b8e989ee6 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/index.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/index.tsx @@ -9,7 +9,6 @@ import { i18n } from '@kbn/i18n'; import { History, Location } from 'history'; import React, { useState } from 'react'; import { useHistory } from 'react-router-dom'; -import { StickyContainer } from 'react-sticky'; import styled from 'styled-components'; import { px } from '../../../../../../style/variables'; import { Timeline } from '../../../../../shared/charts/Timeline'; @@ -128,7 +127,7 @@ export function Waterfall({ })} /> )} - +

    {renderItems(waterfall.childrenByParentId)} - +
    { ], }; - const wrapper = mountWithTheme( - - - - ); + const wrapper = mountWithTheme(); expect(toJson(wrapper)).toMatchSnapshot(); }); @@ -84,12 +79,7 @@ describe('Timeline', () => { }, }; - const mountTimeline = () => - mountWithTheme( - - - - ); + const mountTimeline = () => mountWithTheme(); expect(mountTimeline).not.toThrow(); }); diff --git a/x-pack/plugins/apm/public/components/shared/charts/Timeline/TimelineAxis.tsx b/x-pack/plugins/apm/public/components/shared/charts/Timeline/TimelineAxis.tsx index dcdfee22e3cfc..904917f2f9792 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/Timeline/TimelineAxis.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/Timeline/TimelineAxis.tsx @@ -6,7 +6,6 @@ import React, { ReactNode } from 'react'; import { inRange } from 'lodash'; -import { Sticky } from 'react-sticky'; import { XAxis, XYPlot } from 'react-vis'; import { getDurationFormatter } from '../../../../../common/utils/formatters'; import { useTheme } from '../../../../hooks/use_theme'; @@ -54,57 +53,51 @@ export function TimelineAxis({ const topTraceDurationFormatted = tickFormatter(topTraceDuration).formatted; return ( - - {({ style }) => { - return ( -
    - - tickFormatter(time).formatted} - tickPadding={20} - style={{ - text: { fill: theme.eui.euiColorDarkShade }, - }} - /> +
    + + tickFormatter(time).formatted} + tickPadding={20} + style={{ + text: { fill: theme.eui.euiColorDarkShade }, + }} + /> - {topTraceDuration > 0 && ( - - )} + {topTraceDuration > 0 && ( + + )} - {marks.map((mark) => ( - - ))} - -
    - ); - }} - + {marks.map((mark) => ( + + ))} +
    +
    ); } diff --git a/x-pack/plugins/apm/public/components/shared/charts/Timeline/__snapshots__/Timeline.test.tsx.snap b/x-pack/plugins/apm/public/components/shared/charts/Timeline/__snapshots__/Timeline.test.tsx.snap index 2756de6e384bc..76e2960e78e9d 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/Timeline/__snapshots__/Timeline.test.tsx.snap +++ b/x-pack/plugins/apm/public/components/shared/charts/Timeline/__snapshots__/Timeline.test.tsx.snap @@ -2,18 +2,11 @@ exports[`Timeline should render with data 1`] = `
    -
    -
    + } +/> `; diff --git a/yarn.lock b/yarn.lock index af1a1493bc36a..73741371d10c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5537,13 +5537,6 @@ "@types/history" "*" "@types/react" "*" -"@types/react-sticky@^6.0.3": - version "6.0.3" - resolved "https://registry.yarnpkg.com/@types/react-sticky/-/react-sticky-6.0.3.tgz#94d16a951467b29ad44c224081d9503e7e590434" - integrity sha512-tW0Y1hTr2Tao4yX58iKl0i7BaqrdObGXAzsyzd8VGVrWVEgbQuV6P6QKVd/kFC7FroXyelftiVNJ09pnfkcjww== - dependencies: - "@types/react" "*" - "@types/react-syntax-highlighter@11.0.4": version "11.0.4" resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.4.tgz#d86d17697db62f98046874f62fdb3e53a0bbc4cd" @@ -22892,7 +22885,7 @@ raf-schd@^4.0.0, raf-schd@^4.0.2: resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.2.tgz#bd44c708188f2e84c810bf55fcea9231bcaed8a0" integrity sha512-VhlMZmGy6A6hrkJWHLNTGl5gtgMUm+xfGza6wbwnE914yeQ5Ybm18vgM734RZhMgfw4tacUrWseGZlpUrrakEQ== -raf@^3.1.0, raf@^3.3.0, raf@^3.4.1: +raf@^3.1.0, raf@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== @@ -23570,14 +23563,6 @@ react-sizeme@^2.6.7: shallowequal "^1.1.0" throttle-debounce "^2.1.0" -react-sticky@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/react-sticky/-/react-sticky-6.0.3.tgz#7a18b643e1863da113d7f7036118d2a75d9ecde4" - integrity sha512-LNH4UJlRatOqo29/VHxDZOf6fwbgfgcHO4mkEFvrie5FuaZCSTGtug5R8NGqJ0kSnX8gHw8qZN37FcvnFBJpTQ== - dependencies: - prop-types "^15.5.8" - raf "^3.3.0" - react-style-singleton@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.1.0.tgz#7396885332e9729957f9df51f08cadbfc164e1c4" From 9cbf971427f6b2f4c3ca05b7d526c1ce790e8939 Mon Sep 17 00:00:00 2001 From: Scotty Bollinger Date: Wed, 2 Dec 2020 21:19:52 -0600 Subject: [PATCH 28/40] [Enterprise Search] Fix schema errors button (#84842) * Fix schema errors button When migrated, the button was wrapping the link and it should be the other way around. This caused a blue link color. * Remove redundant true value * TIL EuiButtonTo --- .../indexing_status/indexing_status_errors.test.tsx | 9 ++++----- .../shared/indexing_status/indexing_status_errors.tsx | 11 ++++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_errors.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_errors.test.tsx index fc706aee659a5..563702a143ab3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_errors.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_errors.test.tsx @@ -7,9 +7,9 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { EuiCallOut, EuiButton } from '@elastic/eui'; +import { EuiCallOut } from '@elastic/eui'; -import { EuiLinkTo } from '../react_router_helpers'; +import { EuiButtonTo } from '../react_router_helpers'; import { IndexingStatusErrors } from './indexing_status_errors'; @@ -17,9 +17,8 @@ describe('IndexingStatusErrors', () => { it('renders', () => { const wrapper = shallow(); - expect(wrapper.find(EuiButton)).toHaveLength(1); expect(wrapper.find(EuiCallOut)).toHaveLength(1); - expect(wrapper.find(EuiLinkTo)).toHaveLength(1); - expect(wrapper.find(EuiLinkTo).prop('to')).toEqual('/path'); + expect(wrapper.find(EuiButtonTo)).toHaveLength(1); + expect(wrapper.find(EuiButtonTo).prop('to')).toEqual('/path'); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_errors.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_errors.tsx index a928400b2338c..2be27299fd77f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_errors.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_errors.tsx @@ -6,9 +6,9 @@ import React from 'react'; -import { EuiButton, EuiCallOut } from '@elastic/eui'; +import { EuiCallOut } from '@elastic/eui'; -import { EuiLinkTo } from '../react_router_helpers'; +import { EuiButtonTo } from '../react_router_helpers'; import { INDEXING_STATUS_HAS_ERRORS_TITLE, INDEXING_STATUS_HAS_ERRORS_BUTTON } from './constants'; @@ -24,8 +24,9 @@ export const IndexingStatusErrors: React.FC = ({ vie data-test-subj="IndexingStatusErrors" >

    {INDEXING_STATUS_HAS_ERRORS_TITLE}

    - - {INDEXING_STATUS_HAS_ERRORS_BUTTON} - + + + {INDEXING_STATUS_HAS_ERRORS_BUTTON} + ); From 78123a109d5eefe619ff71a46ebe952512b5daec Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Thu, 3 Dec 2020 09:19:36 +0300 Subject: [PATCH 29/40] Rename server.xsrf.whitelist to server.xsrf.allowlist (#84791) * rename xsrd.whitelist to xsrf.allowlist * update docs * update telemetry schema * update kbn-config tests --- docs/api/using-api.asciidoc | 2 +- docs/apm/api.asciidoc | 2 +- docs/setup/settings.asciidoc | 4 ++-- .../legacy_object_to_config_adapter.test.ts.snap | 4 ++-- .../legacy_object_to_config_adapter.test.ts | 4 ++-- .../config/deprecation/core_deprecations.test.ts | 5 +++-- .../config/deprecation/core_deprecations.ts | 12 +----------- .../core_usage_data_service.mock.ts | 2 +- .../core_usage_data_service.test.ts | 2 +- .../core_usage_data/core_usage_data_service.ts | 2 +- src/core/server/core_usage_data/types.ts | 2 +- .../http/__snapshots__/http_config.test.ts.snap | 2 +- .../server/http/cookie_session_storage.test.ts | 2 +- src/core/server/http/http_config.test.ts | 6 +++--- src/core/server/http/http_config.ts | 4 ++-- .../integration_tests/lifecycle_handlers.test.ts | 8 ++++---- src/core/server/http/lifecycle_handlers.test.ts | 16 ++++++++-------- src/core/server/http/lifecycle_handlers.ts | 4 ++-- src/core/server/http/test_utils.ts | 2 +- src/core/server/server.api.md | 2 +- .../collectors/core/core_usage_collector.ts | 2 +- src/plugins/telemetry/schema/oss_plugins.json | 2 +- .../alerting_api_integration/common/config.ts | 2 +- 23 files changed, 42 insertions(+), 51 deletions(-) diff --git a/docs/api/using-api.asciidoc b/docs/api/using-api.asciidoc index c796aac3d6b27..d66718be4074a 100644 --- a/docs/api/using-api.asciidoc +++ b/docs/api/using-api.asciidoc @@ -61,7 +61,7 @@ For all APIs, you must use a request header. The {kib} APIs support the `kbn-xsr By default, you must use `kbn-xsrf` for all API calls, except in the following scenarios: * The API endpoint uses the `GET` or `HEAD` operations -* The path is whitelisted using the <> setting +* The path is allowed using the <> setting * XSRF protections are disabled using the <> setting `Content-Type: application/json`:: diff --git a/docs/apm/api.asciidoc b/docs/apm/api.asciidoc index 01ba084b9e9e7..d9a8d0558714f 100644 --- a/docs/apm/api.asciidoc +++ b/docs/apm/api.asciidoc @@ -40,7 +40,7 @@ users interacting with APM APIs must have <> setting +* The path is allowed using the <> setting * XSRF protections are disabled using the <> setting `Content-Type: application/json`:: diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index c22d4466ee09e..3786cbc7d83b6 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -575,10 +575,10 @@ all http requests to https over the port configured as <> setting requires the following format: +The <> setting requires the following format: |=== diff --git a/packages/kbn-config/src/legacy/__snapshots__/legacy_object_to_config_adapter.test.ts.snap b/packages/kbn-config/src/legacy/__snapshots__/legacy_object_to_config_adapter.test.ts.snap index 4a6d86a0dfba6..5d8fb1e28beb6 100644 --- a/packages/kbn-config/src/legacy/__snapshots__/legacy_object_to_config_adapter.test.ts.snap +++ b/packages/kbn-config/src/legacy/__snapshots__/legacy_object_to_config_adapter.test.ts.snap @@ -25,8 +25,8 @@ Object { }, "uuid": undefined, "xsrf": Object { + "allowlist": Array [], "disableProtection": false, - "whitelist": Array [], }, } `; @@ -56,8 +56,8 @@ Object { }, "uuid": undefined, "xsrf": Object { + "allowlist": Array [], "disableProtection": false, - "whitelist": Array [], }, } `; diff --git a/packages/kbn-config/src/legacy/legacy_object_to_config_adapter.test.ts b/packages/kbn-config/src/legacy/legacy_object_to_config_adapter.test.ts index 1c51564187442..036ff5e80b3ec 100644 --- a/packages/kbn-config/src/legacy/legacy_object_to_config_adapter.test.ts +++ b/packages/kbn-config/src/legacy/legacy_object_to_config_adapter.test.ts @@ -96,7 +96,7 @@ describe('#get', () => { someNotSupportedValue: 'val', xsrf: { disableProtection: false, - whitelist: [], + allowlist: [], }, }, }); @@ -119,7 +119,7 @@ describe('#get', () => { someNotSupportedValue: 'val', xsrf: { disableProtection: false, - whitelist: [], + allowlist: [], }, }, }); diff --git a/src/core/server/config/deprecation/core_deprecations.test.ts b/src/core/server/config/deprecation/core_deprecations.test.ts index 7a69dc2fa726e..c645629fa5653 100644 --- a/src/core/server/config/deprecation/core_deprecations.test.ts +++ b/src/core/server/config/deprecation/core_deprecations.test.ts @@ -82,12 +82,13 @@ describe('core deprecations', () => { describe('xsrfDeprecation', () => { it('logs a warning if server.xsrf.whitelist is set', () => { - const { messages } = applyCoreDeprecations({ + const { migrated, messages } = applyCoreDeprecations({ server: { xsrf: { whitelist: ['/path'] } }, }); + expect(migrated.server.xsrf.allowlist).toEqual(['/path']); expect(messages).toMatchInlineSnapshot(` Array [ - "It is not recommended to disable xsrf protections for API endpoints via [server.xsrf.whitelist]. It will be removed in 8.0 release. Instead, supply the \\"kbn-xsrf\\" header.", + "\\"server.xsrf.whitelist\\" is deprecated and has been replaced by \\"server.xsrf.allowlist\\"", ] `); }); diff --git a/src/core/server/config/deprecation/core_deprecations.ts b/src/core/server/config/deprecation/core_deprecations.ts index 6c85cfbed8e82..3dde7cfb6c1cb 100644 --- a/src/core/server/config/deprecation/core_deprecations.ts +++ b/src/core/server/config/deprecation/core_deprecations.ts @@ -38,16 +38,6 @@ const dataPathDeprecation: ConfigDeprecation = (settings, fromPath, log) => { return settings; }; -const xsrfDeprecation: ConfigDeprecation = (settings, fromPath, log) => { - if ((settings.server?.xsrf?.whitelist ?? []).length > 0) { - log( - 'It is not recommended to disable xsrf protections for API endpoints via [server.xsrf.whitelist]. ' + - 'It will be removed in 8.0 release. Instead, supply the "kbn-xsrf" header.' - ); - } - return settings; -}; - const rewriteBasePathDeprecation: ConfigDeprecation = (settings, fromPath, log) => { if (has(settings, 'server.basePath') && !has(settings, 'server.rewriteBasePath')) { log( @@ -140,10 +130,10 @@ export const coreDeprecationProvider: ConfigDeprecationProvider = ({ rename, unu unusedFromRoot('elasticsearch.startupTimeout'), rename('cpu.cgroup.path.override', 'ops.cGroupOverrides.cpuPath'), rename('cpuacct.cgroup.path.override', 'ops.cGroupOverrides.cpuAcctPath'), + rename('server.xsrf.whitelist', 'server.xsrf.allowlist'), configPathDeprecation, dataPathDeprecation, rewriteBasePathDeprecation, cspRulesDeprecation, mapManifestServiceUrlDeprecation, - xsrfDeprecation, ]; diff --git a/src/core/server/core_usage_data/core_usage_data_service.mock.ts b/src/core/server/core_usage_data/core_usage_data_service.mock.ts index 523256129333f..b1c731e8ba534 100644 --- a/src/core/server/core_usage_data/core_usage_data_service.mock.ts +++ b/src/core/server/core_usage_data/core_usage_data_service.mock.ts @@ -99,7 +99,7 @@ const createStartContractMock = () => { }, xsrf: { disableProtection: false, - whitelistConfigured: false, + allowlistConfigured: false, }, }, logging: { diff --git a/src/core/server/core_usage_data/core_usage_data_service.test.ts b/src/core/server/core_usage_data/core_usage_data_service.test.ts index e1c78edb902a9..6686a778ee8a5 100644 --- a/src/core/server/core_usage_data/core_usage_data_service.test.ts +++ b/src/core/server/core_usage_data/core_usage_data_service.test.ts @@ -182,8 +182,8 @@ describe('CoreUsageDataService', () => { "truststoreConfigured": false, }, "xsrf": Object { + "allowlistConfigured": false, "disableProtection": false, - "whitelistConfigured": false, }, }, "logging": Object { diff --git a/src/core/server/core_usage_data/core_usage_data_service.ts b/src/core/server/core_usage_data/core_usage_data_service.ts index f729e23cb68bc..490c411ecb852 100644 --- a/src/core/server/core_usage_data/core_usage_data_service.ts +++ b/src/core/server/core_usage_data/core_usage_data_service.ts @@ -180,7 +180,7 @@ export class CoreUsageDataService implements CoreService { expect(validated.name).toEqual('kibana-hostname'); }); -test('throws if xsrf.whitelist element does not start with a slash', () => { +test('throws if xsrf.allowlist element does not start with a slash', () => { const httpSchema = config.schema; const obj = { xsrf: { - whitelist: ['/valid-path', 'invalid-path'], + allowlist: ['/valid-path', 'invalid-path'], }, }; expect(() => httpSchema.validate(obj)).toThrowErrorMatchingInlineSnapshot( - `"[xsrf.whitelist.1]: must start with a slash"` + `"[xsrf.allowlist.1]: must start with a slash"` ); }); diff --git a/src/core/server/http/http_config.ts b/src/core/server/http/http_config.ts index 7d41b4ea9e915..be64def294625 100644 --- a/src/core/server/http/http_config.ts +++ b/src/core/server/http/http_config.ts @@ -82,7 +82,7 @@ export const config = { ), xsrf: schema.object({ disableProtection: schema.boolean({ defaultValue: false }), - whitelist: schema.arrayOf( + allowlist: schema.arrayOf( schema.string({ validate: match(/^\//, 'must start with a slash') }), { defaultValue: [] } ), @@ -142,7 +142,7 @@ export class HttpConfig { public ssl: SslConfig; public compression: { enabled: boolean; referrerWhitelist?: string[] }; public csp: ICspConfig; - public xsrf: { disableProtection: boolean; whitelist: string[] }; + public xsrf: { disableProtection: boolean; allowlist: string[] }; public requestId: { allowFromAnyIp: boolean; ipAllowlist: string[] }; /** diff --git a/src/core/server/http/integration_tests/lifecycle_handlers.test.ts b/src/core/server/http/integration_tests/lifecycle_handlers.test.ts index a964130550bf5..7df35b04c66cf 100644 --- a/src/core/server/http/integration_tests/lifecycle_handlers.test.ts +++ b/src/core/server/http/integration_tests/lifecycle_handlers.test.ts @@ -36,7 +36,7 @@ const actualVersion = pkg.version; const versionHeader = 'kbn-version'; const xsrfHeader = 'kbn-xsrf'; const nameHeader = 'kbn-name'; -const whitelistedTestPath = '/xsrf/test/route/whitelisted'; +const allowlistedTestPath = '/xsrf/test/route/whitelisted'; const xsrfDisabledTestPath = '/xsrf/test/route/disabled'; const kibanaName = 'my-kibana-name'; const setupDeps = { @@ -63,7 +63,7 @@ describe('core lifecycle handlers', () => { customResponseHeaders: { 'some-header': 'some-value', }, - xsrf: { disableProtection: false, whitelist: [whitelistedTestPath] }, + xsrf: { disableProtection: false, allowlist: [allowlistedTestPath] }, requestId: { allowFromAnyIp: true, ipAllowlist: [], @@ -179,7 +179,7 @@ describe('core lifecycle handlers', () => { } ); ((router as any)[method.toLowerCase()] as RouteRegistrar)( - { path: whitelistedTestPath, validate: false }, + { path: allowlistedTestPath, validate: false }, (context, req, res) => { return res.ok({ body: 'ok' }); } @@ -235,7 +235,7 @@ describe('core lifecycle handlers', () => { }); it('accepts whitelisted requests without either an xsrf or version header', async () => { - await getSupertest(method.toLowerCase(), whitelistedTestPath).expect(200, 'ok'); + await getSupertest(method.toLowerCase(), allowlistedTestPath).expect(200, 'ok'); }); it('accepts requests on a route with disabled xsrf protection', async () => { diff --git a/src/core/server/http/lifecycle_handlers.test.ts b/src/core/server/http/lifecycle_handlers.test.ts index fdcf2a173b906..8ad823b3a6944 100644 --- a/src/core/server/http/lifecycle_handlers.test.ts +++ b/src/core/server/http/lifecycle_handlers.test.ts @@ -58,7 +58,7 @@ describe('xsrf post-auth handler', () => { describe('non destructive methods', () => { it('accepts requests without version or xsrf header', () => { - const config = createConfig({ xsrf: { whitelist: [], disableProtection: false } }); + const config = createConfig({ xsrf: { allowlist: [], disableProtection: false } }); const handler = createXsrfPostAuthHandler(config); const request = forgeRequest({ method: 'get', headers: {} }); @@ -74,7 +74,7 @@ describe('xsrf post-auth handler', () => { describe('destructive methods', () => { it('accepts requests with xsrf header', () => { - const config = createConfig({ xsrf: { whitelist: [], disableProtection: false } }); + const config = createConfig({ xsrf: { allowlist: [], disableProtection: false } }); const handler = createXsrfPostAuthHandler(config); const request = forgeRequest({ method: 'post', headers: { 'kbn-xsrf': 'xsrf' } }); @@ -88,7 +88,7 @@ describe('xsrf post-auth handler', () => { }); it('accepts requests with version header', () => { - const config = createConfig({ xsrf: { whitelist: [], disableProtection: false } }); + const config = createConfig({ xsrf: { allowlist: [], disableProtection: false } }); const handler = createXsrfPostAuthHandler(config); const request = forgeRequest({ method: 'post', headers: { 'kbn-version': 'some-version' } }); @@ -102,7 +102,7 @@ describe('xsrf post-auth handler', () => { }); it('returns a bad request if called without xsrf or version header', () => { - const config = createConfig({ xsrf: { whitelist: [], disableProtection: false } }); + const config = createConfig({ xsrf: { allowlist: [], disableProtection: false } }); const handler = createXsrfPostAuthHandler(config); const request = forgeRequest({ method: 'post' }); @@ -121,7 +121,7 @@ describe('xsrf post-auth handler', () => { }); it('accepts requests if protection is disabled', () => { - const config = createConfig({ xsrf: { whitelist: [], disableProtection: true } }); + const config = createConfig({ xsrf: { allowlist: [], disableProtection: true } }); const handler = createXsrfPostAuthHandler(config); const request = forgeRequest({ method: 'post', headers: {} }); @@ -134,9 +134,9 @@ describe('xsrf post-auth handler', () => { expect(result).toEqual('next'); }); - it('accepts requests if path is whitelisted', () => { + it('accepts requests if path is allowlisted', () => { const config = createConfig({ - xsrf: { whitelist: ['/some-path'], disableProtection: false }, + xsrf: { allowlist: ['/some-path'], disableProtection: false }, }); const handler = createXsrfPostAuthHandler(config); const request = forgeRequest({ method: 'post', headers: {}, path: '/some-path' }); @@ -152,7 +152,7 @@ describe('xsrf post-auth handler', () => { it('accepts requests if xsrf protection on a route is disabled', () => { const config = createConfig({ - xsrf: { whitelist: [], disableProtection: false }, + xsrf: { allowlist: [], disableProtection: false }, }); const handler = createXsrfPostAuthHandler(config); const request = forgeRequest({ diff --git a/src/core/server/http/lifecycle_handlers.ts b/src/core/server/http/lifecycle_handlers.ts index 7ef7e86326039..4060284b5b56a 100644 --- a/src/core/server/http/lifecycle_handlers.ts +++ b/src/core/server/http/lifecycle_handlers.ts @@ -29,12 +29,12 @@ const XSRF_HEADER = 'kbn-xsrf'; const KIBANA_NAME_HEADER = 'kbn-name'; export const createXsrfPostAuthHandler = (config: HttpConfig): OnPostAuthHandler => { - const { whitelist, disableProtection } = config.xsrf; + const { allowlist, disableProtection } = config.xsrf; return (request, response, toolkit) => { if ( disableProtection || - whitelist.includes(request.route.path) || + allowlist.includes(request.route.path) || request.route.options.xsrfRequired === false ) { return toolkit.next(); diff --git a/src/core/server/http/test_utils.ts b/src/core/server/http/test_utils.ts index 412396644648e..cdcbe513e1224 100644 --- a/src/core/server/http/test_utils.ts +++ b/src/core/server/http/test_utils.ts @@ -43,7 +43,7 @@ configService.atPath.mockReturnValue( compression: { enabled: true }, xsrf: { disableProtection: true, - whitelist: [], + allowlist: [], }, customResponseHeaders: {}, requestId: { diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 36a8d9a52fd52..59f9c4f9ff38c 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -409,7 +409,7 @@ export interface CoreConfigUsageData { }; xsrf: { disableProtection: boolean; - whitelistConfigured: boolean; + allowlistConfigured: boolean; }; requestId: { allowFromAnyIp: boolean; diff --git a/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts b/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts index 3fd011b0bded2..a514f9f899e55 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts @@ -65,7 +65,7 @@ export function getCoreUsageCollector( }, xsrf: { disableProtection: { type: 'boolean' }, - whitelistConfigured: { type: 'boolean' }, + allowlistConfigured: { type: 'boolean' }, }, requestId: { allowFromAnyIp: { type: 'boolean' }, diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 3d79d7c6cf0e1..e1078c60caf2e 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -1391,7 +1391,7 @@ "disableProtection": { "type": "boolean" }, - "whitelistConfigured": { + "allowlistConfigured": { "type": "boolean" } } diff --git a/x-pack/test/alerting_api_integration/common/config.ts b/x-pack/test/alerting_api_integration/common/config.ts index cb78e76bdd697..866dd0581b548 100644 --- a/x-pack/test/alerting_api_integration/common/config.ts +++ b/x-pack/test/alerting_api_integration/common/config.ts @@ -143,7 +143,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) (pluginDir) => `--plugin-path=${path.resolve(__dirname, 'fixtures', 'plugins', pluginDir)}` ), - `--server.xsrf.whitelist=${JSON.stringify(getAllExternalServiceSimulatorPaths())}`, + `--server.xsrf.allowlist=${JSON.stringify(getAllExternalServiceSimulatorPaths())}`, ...(ssl ? [ `--elasticsearch.hosts=${servers.elasticsearch.protocol}://${servers.elasticsearch.hostname}:${servers.elasticsearch.port}`, From 155d150a089b99f35506567aea36b41d70cccd19 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 3 Dec 2020 09:23:14 +0100 Subject: [PATCH 30/40] [Discover] Unskip date histogram test (#84727) --- test/functional/apps/discover/_discover_histogram.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/functional/apps/discover/_discover_histogram.ts b/test/functional/apps/discover/_discover_histogram.ts index 672becca614c9..e06783174e83b 100644 --- a/test/functional/apps/discover/_discover_histogram.ts +++ b/test/functional/apps/discover/_discover_histogram.ts @@ -31,8 +31,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'dateFormat:tz': 'Europe/Berlin', }; - // FLAKY: https://github.com/elastic/kibana/issues/81576 - describe.skip('discover histogram', function describeIndexTests() { + describe('discover histogram', function describeIndexTests() { before(async () => { await esArchiver.loadIfNeeded('logstash_functional'); await esArchiver.load('long_window_logstash'); From 770a00530d8afd8bf534d4e559137844821e3334 Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Thu, 3 Dec 2020 09:33:48 +0100 Subject: [PATCH 31/40] Catch @hapi/podium errors (#84575) --- .../src/legacy_logging_server.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/kbn-legacy-logging/src/legacy_logging_server.ts b/packages/kbn-legacy-logging/src/legacy_logging_server.ts index 1b13eda44fff2..1533bde4fc17b 100644 --- a/packages/kbn-legacy-logging/src/legacy_logging_server.ts +++ b/packages/kbn-legacy-logging/src/legacy_logging_server.ts @@ -117,11 +117,18 @@ export class LegacyLoggingServer { public log({ level, context, message, error, timestamp, meta = {} }: LogRecord) { const { tags = [], ...metadata } = meta; - this.events.emit('log', { - data: getDataToLog(error, metadata, message), - tags: [getLegacyLogLevel(level), ...context.split('.'), ...tags], - timestamp: timestamp.getTime(), - }); + this.events + .emit('log', { + data: getDataToLog(error, metadata, message), + tags: [getLegacyLogLevel(level), ...context.split('.'), ...tags], + timestamp: timestamp.getTime(), + }) + // @ts-expect-error @hapi/podium emit is actually an async function + .catch((err) => { + // eslint-disable-next-line no-console + console.error('An unexpected error occurred while writing to the log:', err.stack); + process.exit(1); + }); } public stop() { From 3ae73653ff8ca59f2731415f506d414f486db031 Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Thu, 3 Dec 2020 09:34:45 +0100 Subject: [PATCH 32/40] Improve logging pipeline in @kbn/legacy-logging (#84629) --- .../src/log_reporter.test.ts | 142 ++++++++++++++++++ .../kbn-legacy-logging/src/log_reporter.ts | 29 ++-- 2 files changed, 161 insertions(+), 10 deletions(-) create mode 100644 packages/kbn-legacy-logging/src/log_reporter.test.ts diff --git a/packages/kbn-legacy-logging/src/log_reporter.test.ts b/packages/kbn-legacy-logging/src/log_reporter.test.ts new file mode 100644 index 0000000000000..4fa2922c7824e --- /dev/null +++ b/packages/kbn-legacy-logging/src/log_reporter.test.ts @@ -0,0 +1,142 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import os from 'os'; +import path from 'path'; +import fs from 'fs'; + +import stripAnsi from 'strip-ansi'; + +import { getLogReporter } from './log_reporter'; + +const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + +describe('getLogReporter', () => { + it('should log to stdout (not json)', async () => { + const lines: string[] = []; + const origWrite = process.stdout.write; + process.stdout.write = (buffer: string | Uint8Array): boolean => { + lines.push(stripAnsi(buffer.toString()).trim()); + return true; + }; + + const loggerStream = getLogReporter({ + config: { + json: false, + dest: 'stdout', + filter: {}, + }, + events: { log: '*' }, + }); + + loggerStream.end({ event: 'log', tags: ['foo'], data: 'hello world' }); + + await sleep(500); + + process.stdout.write = origWrite; + expect(lines.length).toBe(1); + expect(lines[0]).toMatch(/^log \[[^\]]*\] \[foo\] hello world$/); + }); + + it('should log to stdout (as json)', async () => { + const lines: string[] = []; + const origWrite = process.stdout.write; + process.stdout.write = (buffer: string | Uint8Array): boolean => { + lines.push(JSON.parse(buffer.toString().trim())); + return true; + }; + + const loggerStream = getLogReporter({ + config: { + json: true, + dest: 'stdout', + filter: {}, + }, + events: { log: '*' }, + }); + + loggerStream.end({ event: 'log', tags: ['foo'], data: 'hello world' }); + + await sleep(500); + + process.stdout.write = origWrite; + expect(lines.length).toBe(1); + expect(lines[0]).toMatchObject({ + type: 'log', + tags: ['foo'], + message: 'hello world', + }); + }); + + it('should log to custom file (not json)', async () => { + const dir = os.tmpdir(); + const logfile = `dest-${Date.now()}.log`; + const dest = path.join(dir, logfile); + + const loggerStream = getLogReporter({ + config: { + json: false, + dest, + filter: {}, + }, + events: { log: '*' }, + }); + + loggerStream.end({ event: 'log', tags: ['foo'], data: 'hello world' }); + + await sleep(500); + + const lines = stripAnsi(fs.readFileSync(dest, { encoding: 'utf8' })) + .trim() + .split(os.EOL); + expect(lines.length).toBe(1); + expect(lines[0]).toMatch(/^log \[[^\]]*\] \[foo\] hello world$/); + }); + + it('should log to custom file (as json)', async () => { + const dir = os.tmpdir(); + const logfile = `dest-${Date.now()}.log`; + const dest = path.join(dir, logfile); + + const loggerStream = getLogReporter({ + config: { + json: true, + dest, + filter: {}, + }, + events: { log: '*' }, + }); + + loggerStream.end({ event: 'log', tags: ['foo'], data: 'hello world' }); + + await sleep(500); + + const lines = fs + .readFileSync(dest, { encoding: 'utf8' }) + .trim() + .split(os.EOL) + .map((data) => JSON.parse(data)); + expect(lines.length).toBe(1); + expect(lines[0]).toMatchObject({ + type: 'log', + tags: ['foo'], + message: 'hello world', + }); + }); +}); diff --git a/packages/kbn-legacy-logging/src/log_reporter.ts b/packages/kbn-legacy-logging/src/log_reporter.ts index 8ecaf348bac04..f0075b431b83d 100644 --- a/packages/kbn-legacy-logging/src/log_reporter.ts +++ b/packages/kbn-legacy-logging/src/log_reporter.ts @@ -17,9 +17,11 @@ * under the License. */ +import { createWriteStream } from 'fs'; +import { pipeline } from 'stream'; + // @ts-expect-error missing type def import { Squeeze } from '@hapi/good-squeeze'; -import { createWriteStream as writeStr, WriteStream } from 'fs'; import { KbnLoggerJsonFormat } from './log_format_json'; import { KbnLoggerStringFormat } from './log_format_string'; @@ -31,21 +33,28 @@ export function getLogReporter({ events, config }: { events: any; config: LogFor const format = config.json ? new KbnLoggerJsonFormat(config) : new KbnLoggerStringFormat(config); const logInterceptor = new LogInterceptor(); - let dest: WriteStream | NodeJS.WritableStream; if (config.dest === 'stdout') { - dest = process.stdout; + pipeline(logInterceptor, squeeze, format, onFinished); + // The `pipeline` function is used to properly close all streams in the + // pipeline in case one of them ends or fails. Since stdout obviously + // shouldn't be closed in case of a failure in one of the other streams, + // we're not including that in the call to `pipeline`, but rely on the old + // `pipe` function instead. + format.pipe(process.stdout); } else { - dest = writeStr(config.dest, { + const dest = createWriteStream(config.dest, { flags: 'a', encoding: 'utf8', }); - - logInterceptor.on('end', () => { - dest.end(); - }); + pipeline(logInterceptor, squeeze, format, dest, onFinished); } - logInterceptor.pipe(squeeze).pipe(format).pipe(dest); - return logInterceptor; } + +function onFinished(err: NodeJS.ErrnoException | null) { + if (err) { + // eslint-disable-next-line no-console + console.error('An unexpected error occurred in the logging pipeline:', err.stack); + } +} From 9041ea5478b2e7b31f2de1270f00d2a11530f4ac Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 3 Dec 2020 09:41:45 +0100 Subject: [PATCH 33/40] [Lens] Migrate legacy es client and remove total hits as int (#84340) --- .../lens/server/routes/existing_fields.ts | 11 +++--- .../plugins/lens/server/routes/field_stats.ts | 35 +++++++++---------- .../plugins/lens/server/routes/telemetry.ts | 3 +- x-pack/plugins/lens/server/usage/task.ts | 28 +++++++-------- .../lens/server/usage/visualization_counts.ts | 8 ++--- .../api_integration/apis/lens/telemetry.ts | 28 +++++++-------- 6 files changed, 54 insertions(+), 59 deletions(-) diff --git a/x-pack/plugins/lens/server/routes/existing_fields.ts b/x-pack/plugins/lens/server/routes/existing_fields.ts index aef8b1b3d7076..43c56af7f71bc 100644 --- a/x-pack/plugins/lens/server/routes/existing_fields.ts +++ b/x-pack/plugins/lens/server/routes/existing_fields.ts @@ -5,8 +5,9 @@ */ import Boom from '@hapi/boom'; +import { errors } from '@elastic/elasticsearch'; import { schema } from '@kbn/config-schema'; -import { ILegacyScopedClusterClient, RequestHandlerContext } from 'src/core/server'; +import { RequestHandlerContext, ElasticsearchClient } from 'src/core/server'; import { CoreSetup, Logger } from 'src/core/server'; import { IndexPattern, IndexPatternsService } from 'src/plugins/data/common'; import { BASE_API_URL } from '../../common'; @@ -68,7 +69,7 @@ export async function existingFieldsRoute(setup: CoreSetup, logger.info( `Field existence check failed: ${isBoomError(e) ? e.output.payload.message : e.message}` ); - if (e.status === 404) { + if (e instanceof errors.ResponseError && e.statusCode === 404) { return res.notFound({ body: e.message }); } if (isBoomError(e)) { @@ -111,7 +112,7 @@ async function fetchFieldExistence({ fromDate, toDate, dslQuery, - client: context.core.elasticsearch.legacy.client, + client: context.core.elasticsearch.client.asCurrentUser, index: indexPattern.title, timeFieldName: timeFieldName || indexPattern.timeFieldName, fields, @@ -149,7 +150,7 @@ async function fetchIndexPatternStats({ toDate, fields, }: { - client: ILegacyScopedClusterClient; + client: ElasticsearchClient; index: string; dslQuery: object; timeFieldName?: string; @@ -179,7 +180,7 @@ async function fetchIndexPatternStats({ }; const scriptedFields = fields.filter((f) => f.isScript); - const result = await client.callAsCurrentUser('search', { + const { body: result } = await client.search({ index, body: { size: SAMPLE_SIZE, diff --git a/x-pack/plugins/lens/server/routes/field_stats.ts b/x-pack/plugins/lens/server/routes/field_stats.ts index e0f1e05ed970d..21dfb90ec0ff4 100644 --- a/x-pack/plugins/lens/server/routes/field_stats.ts +++ b/x-pack/plugins/lens/server/routes/field_stats.ts @@ -5,6 +5,7 @@ */ import Boom from '@hapi/boom'; +import { errors } from '@elastic/elasticsearch'; import DateMath from '@elastic/datemath'; import { schema } from '@kbn/config-schema'; import { CoreSetup } from 'src/core/server'; @@ -47,7 +48,7 @@ export async function initFieldsRoute(setup: CoreSetup) { }, }, async (context, req, res) => { - const requestClient = context.core.elasticsearch.legacy.client; + const requestClient = context.core.elasticsearch.client.asCurrentUser; const { fromDate, toDate, timeFieldName, field, dslQuery } = req.body; try { @@ -71,18 +72,18 @@ export async function initFieldsRoute(setup: CoreSetup) { }, }; - const search = (aggs: unknown) => - requestClient.callAsCurrentUser('search', { + const search = async (aggs: unknown) => { + const { body: result } = await requestClient.search({ index: req.params.indexPatternTitle, + track_total_hits: true, body: { query, aggs, }, - // The hits total changed in 7.0 from number to object, unless this flag is set - // this is a workaround for elasticsearch response types that are from 6.x - restTotalHitsAsInt: true, size: 0, }); + return result; + }; if (field.type === 'number') { return res.ok({ @@ -98,7 +99,7 @@ export async function initFieldsRoute(setup: CoreSetup) { body: await getStringSamples(search, field), }); } catch (e) { - if (e.status === 404) { + if (e instanceof errors.ResponseError && e.statusCode === 404) { return res.notFound(); } if (e.isBoom) { @@ -142,8 +143,7 @@ export async function getNumberHistogram( const minMaxResult = (await aggSearchWithBody(searchBody)) as ESSearchResponse< unknown, - { body: { aggs: typeof searchBody } }, - { restTotalHitsAsInt: true } + { body: { aggs: typeof searchBody } } >; const minValue = minMaxResult.aggregations!.sample.min_value.value; @@ -164,7 +164,7 @@ export async function getNumberHistogram( if (histogramInterval === 0) { return { - totalDocuments: minMaxResult.hits.total, + totalDocuments: minMaxResult.hits.total.value, sampledValues: minMaxResult.aggregations!.sample.sample_count.value!, sampledDocuments: minMaxResult.aggregations!.sample.doc_count, topValues: topValuesBuckets, @@ -187,12 +187,11 @@ export async function getNumberHistogram( }; const histogramResult = (await aggSearchWithBody(histogramBody)) as ESSearchResponse< unknown, - { body: { aggs: typeof histogramBody } }, - { restTotalHitsAsInt: true } + { body: { aggs: typeof histogramBody } } >; return { - totalDocuments: minMaxResult.hits.total, + totalDocuments: minMaxResult.hits.total.value, sampledDocuments: minMaxResult.aggregations!.sample.doc_count, sampledValues: minMaxResult.aggregations!.sample.sample_count.value!, histogram: { @@ -227,12 +226,11 @@ export async function getStringSamples( }; const topValuesResult = (await aggSearchWithBody(topValuesBody)) as ESSearchResponse< unknown, - { body: { aggs: typeof topValuesBody } }, - { restTotalHitsAsInt: true } + { body: { aggs: typeof topValuesBody } } >; return { - totalDocuments: topValuesResult.hits.total, + totalDocuments: topValuesResult.hits.total.value, sampledDocuments: topValuesResult.aggregations!.sample.doc_count, sampledValues: topValuesResult.aggregations!.sample.sample_count.value!, topValues: { @@ -275,12 +273,11 @@ export async function getDateHistogram( }; const results = (await aggSearchWithBody(histogramBody)) as ESSearchResponse< unknown, - { body: { aggs: typeof histogramBody } }, - { restTotalHitsAsInt: true } + { body: { aggs: typeof histogramBody } } >; return { - totalDocuments: results.hits.total, + totalDocuments: results.hits.total.value, histogram: { buckets: results.aggregations!.histo.buckets.map((bucket) => ({ count: bucket.doc_count, diff --git a/x-pack/plugins/lens/server/routes/telemetry.ts b/x-pack/plugins/lens/server/routes/telemetry.ts index 820e32509923e..2bd891e7c1376 100644 --- a/x-pack/plugins/lens/server/routes/telemetry.ts +++ b/x-pack/plugins/lens/server/routes/telemetry.ts @@ -5,6 +5,7 @@ */ import Boom from '@hapi/boom'; +import { errors } from '@elastic/elasticsearch'; import { CoreSetup } from 'src/core/server'; import { schema } from '@kbn/config-schema'; import { BASE_API_URL } from '../../common'; @@ -71,7 +72,7 @@ export async function initLensUsageRoute(setup: CoreSetup) return res.ok({ body: {} }); } catch (e) { - if (e.status === 404) { + if (e instanceof errors.ResponseError && e.statusCode === 404) { return res.notFound(); } if (e.isBoom) { diff --git a/x-pack/plugins/lens/server/usage/task.ts b/x-pack/plugins/lens/server/usage/task.ts index 014193fb6566e..0fd797bba68e4 100644 --- a/x-pack/plugins/lens/server/usage/task.ts +++ b/x-pack/plugins/lens/server/usage/task.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LegacyAPICaller, CoreSetup, Logger } from 'kibana/server'; +import { CoreSetup, Logger, ElasticsearchClient } from 'kibana/server'; import { Observable } from 'rxjs'; import { first } from 'rxjs/operators'; import moment from 'moment'; @@ -69,11 +69,12 @@ async function scheduleTasks(logger: Logger, taskManager: TaskManagerStartContra export async function getDailyEvents( kibanaIndex: string, - callCluster: LegacyAPICaller + getEsClient: () => Promise ): Promise<{ byDate: Record>; suggestionsByDate: Record>; }> { + const esClient = await getEsClient(); const aggs = { daily: { date_histogram: { @@ -114,15 +115,10 @@ export async function getDailyEvents( }, }; - const metrics: ESSearchResponse< - unknown, - { - body: { aggs: typeof aggs }; - }, - { restTotalHitsAsInt: true } - > = await callCluster('search', { + const { body: metrics } = await esClient.search< + ESSearchResponse + >({ index: kibanaIndex, - rest_total_hits_as_int: true, body: { query: { bool: { @@ -156,9 +152,9 @@ export async function getDailyEvents( }); // Always delete old date because we don't report it - await callCluster('deleteByQuery', { + await esClient.deleteByQuery({ index: kibanaIndex, - waitForCompletion: true, + wait_for_completion: true, body: { query: { bool: { @@ -184,9 +180,9 @@ export function telemetryTaskRunner( ) { return ({ taskInstance }: RunContext) => { const { state } = taskInstance; - const callCluster = async (...args: Parameters) => { + const getEsClient = async () => { const [coreStart] = await core.getStartServices(); - return coreStart.elasticsearch.legacy.client.callAsInternalUser(...args); + return coreStart.elasticsearch.client.asInternalUser; }; return { @@ -194,8 +190,8 @@ export function telemetryTaskRunner( const kibanaIndex = (await config.pipe(first()).toPromise()).kibana.index; return Promise.all([ - getDailyEvents(kibanaIndex, callCluster), - getVisualizationCounts(callCluster, kibanaIndex), + getDailyEvents(kibanaIndex, getEsClient), + getVisualizationCounts(getEsClient, kibanaIndex), ]) .then(([lensTelemetry, lensVisualizations]) => { return { diff --git a/x-pack/plugins/lens/server/usage/visualization_counts.ts b/x-pack/plugins/lens/server/usage/visualization_counts.ts index c9cd4aff72b2b..f6858ef941b78 100644 --- a/x-pack/plugins/lens/server/usage/visualization_counts.ts +++ b/x-pack/plugins/lens/server/usage/visualization_counts.ts @@ -4,16 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LegacyAPICaller } from 'kibana/server'; +import { ElasticsearchClient } from 'kibana/server'; import { LensVisualizationUsage } from './types'; export async function getVisualizationCounts( - callCluster: LegacyAPICaller, + getEsClient: () => Promise, kibanaIndex: string ): Promise { - const results = await callCluster('search', { + const esClient = await getEsClient(); + const { body: results } = await esClient.search({ index: kibanaIndex, - rest_total_hits_as_int: true, body: { query: { bool: { diff --git a/x-pack/test/api_integration/apis/lens/telemetry.ts b/x-pack/test/api_integration/apis/lens/telemetry.ts index 5525a82b02ee8..d352d250aee69 100644 --- a/x-pack/test/api_integration/apis/lens/telemetry.ts +++ b/x-pack/test/api_integration/apis/lens/telemetry.ts @@ -6,8 +6,7 @@ import moment from 'moment'; import expect from '@kbn/expect'; -import { Client, SearchParams } from 'elasticsearch'; -import { LegacyAPICaller } from 'kibana/server'; +import { Client } from '@elastic/elasticsearch'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -20,10 +19,7 @@ const COMMON_HEADERS = { export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); - const es: Client = getService('legacyEs'); - const callCluster: LegacyAPICaller = (((path: 'search', searchParams: SearchParams) => { - return es[path].call(es, searchParams); - }) as unknown) as LegacyAPICaller; + const es: Client = getService('es'); async function assertExpectedSavedObjects(num: number) { // Make sure that new/deleted docs are available to search @@ -31,7 +27,9 @@ export default ({ getService }: FtrProviderContext) => { index: '.kibana', }); - const { count } = await es.count({ + const { + body: { count }, + } = await es.count({ index: '.kibana', q: 'type:lens-ui-telemetry', }); @@ -44,8 +42,9 @@ export default ({ getService }: FtrProviderContext) => { await es.deleteByQuery({ index: '.kibana', q: 'type:lens-ui-telemetry', - waitForCompletion: true, - refresh: 'wait_for', + wait_for_completion: true, + refresh: true, + body: {}, }); }); @@ -53,8 +52,9 @@ export default ({ getService }: FtrProviderContext) => { await es.deleteByQuery({ index: '.kibana', q: 'type:lens-ui-telemetry', - waitForCompletion: true, - refresh: 'wait_for', + wait_for_completion: true, + refresh: true, + body: {}, }); }); @@ -107,7 +107,7 @@ export default ({ getService }: FtrProviderContext) => { refresh: 'wait_for', }); - const result = await getDailyEvents('.kibana', callCluster); + const result = await getDailyEvents('.kibana', () => Promise.resolve(es)); expect(result).to.eql({ byDate: {}, @@ -150,7 +150,7 @@ export default ({ getService }: FtrProviderContext) => { ], }); - const result = await getDailyEvents('.kibana', callCluster); + const result = await getDailyEvents('.kibana', () => Promise.resolve(es)); expect(result).to.eql({ byDate: { @@ -177,7 +177,7 @@ export default ({ getService }: FtrProviderContext) => { await esArchiver.loadIfNeeded('lens/basic'); - const results = await getVisualizationCounts(callCluster, '.kibana'); + const results = await getVisualizationCounts(() => Promise.resolve(es), '.kibana'); expect(results).to.have.keys([ 'saved_overall', From f1495eda1f063b6910837d1fc2d40ad1cc2577c7 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Thu, 3 Dec 2020 12:34:31 +0300 Subject: [PATCH 34/40] [TSVB] [Cleanup] Remove extra dateFormat props (#84749) --- .../public/application/components/markdown_editor.js | 6 +++--- .../public/application/components/panel_config.js | 2 +- .../application/components/panel_config/markdown.js | 1 - .../components/timeseries_visualization.tsx | 1 - .../public/application/components/vis_editor.js | 1 - .../public/application/components/vis_types/index.ts | 1 - .../application/components/vis_types/markdown/vis.js | 5 ++--- .../components/vis_types/timeseries/vis.js | 12 +++++------- 8 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/plugins/vis_type_timeseries/public/application/components/markdown_editor.js b/src/plugins/vis_type_timeseries/public/application/components/markdown_editor.js index 904a27dcb23c2..4b5038b82f480 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/markdown_editor.js +++ b/src/plugins/vis_type_timeseries/public/application/components/markdown_editor.js @@ -49,12 +49,12 @@ export class MarkdownEditor extends Component { } render() { - const { visData, model, dateFormat } = this.props; + const { visData, model, getConfig } = this.props; if (!visData) { return null; } - + const dateFormat = getConfig('dateFormat'); const series = _.get(visData, `${model.id}.series`, []); const variables = convertSeriesToVars(series, model, dateFormat, this.props.getConfig); const rows = []; @@ -214,6 +214,6 @@ export class MarkdownEditor extends Component { MarkdownEditor.propTypes = { onChange: PropTypes.func, model: PropTypes.object, - dateFormat: PropTypes.string, + getConfig: PropTypes.func, visData: PropTypes.object, }; diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config.js b/src/plugins/vis_type_timeseries/public/application/components/panel_config.js index 3b081d8eb7db9..999127a9eb556 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/panel_config.js +++ b/src/plugins/vis_type_timeseries/public/application/components/panel_config.js @@ -90,6 +90,6 @@ PanelConfig.propTypes = { fields: PropTypes.object, model: PropTypes.object, onChange: PropTypes.func, - dateFormat: PropTypes.string, visData$: PropTypes.object, + getConfig: PropTypes.func, }; diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/markdown.js b/src/plugins/vis_type_timeseries/public/application/components/panel_config/markdown.js index 36d0e3a80e227..ef7aec61a2f0d 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/panel_config/markdown.js +++ b/src/plugins/vis_type_timeseries/public/application/components/panel_config/markdown.js @@ -334,7 +334,6 @@ MarkdownPanelConfigUi.propTypes = { fields: PropTypes.object, model: PropTypes.object, onChange: PropTypes.func, - dateFormat: PropTypes.string, }; export const MarkdownPanelConfig = injectI18n(MarkdownPanelConfigUi); diff --git a/src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx b/src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx index 5b5c99b970854..454f6ff855b38 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx @@ -96,7 +96,6 @@ function TimeseriesVisualization({ if (VisComponent) { return ( diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/index.ts b/src/plugins/vis_type_timeseries/public/application/components/vis_types/index.ts index 56e58b4da3458..e6104ad08fe9e 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/index.ts +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/index.ts @@ -64,6 +64,5 @@ export interface TimeseriesVisProps { ) => void; uiState: PersistedState; visData: TimeseriesVisData; - dateFormat: string; getConfig: IUiSettingsClient['get']; } diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/vis.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/vis.js index e68b9e5ed8467..ffc6bf0dda2d4 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/vis.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/vis.js @@ -31,9 +31,9 @@ import { isBackgroundInverted } from '../../../lib/set_is_reversed'; const getMarkdownId = (id) => `markdown-${id}`; function MarkdownVisualization(props) { - const { backgroundColor, model, visData, dateFormat } = props; + const { backgroundColor, model, visData, getConfig } = props; const series = get(visData, `${model.id}.series`, []); - const variables = convertSeriesToVars(series, model, dateFormat, props.getConfig); + const variables = convertSeriesToVars(series, model, getConfig('dateFormat'), props.getConfig); const markdownElementId = getMarkdownId(uuid.v1()); const panelBackgroundColor = model.background_color || backgroundColor; @@ -103,7 +103,6 @@ MarkdownVisualization.propTypes = { onBrush: PropTypes.func, onChange: PropTypes.func, visData: PropTypes.object, - dateFormat: PropTypes.string, getConfig: PropTypes.func, }; diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js index b752699fa1548..41837fbfb1d21 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js @@ -39,20 +39,18 @@ class TimeseriesVisualization extends Component { model: PropTypes.object, onBrush: PropTypes.func, visData: PropTypes.object, - dateFormat: PropTypes.string, getConfig: PropTypes.func, }; - xAxisFormatter = (interval) => (val) => { - const scaledDataFormat = this.props.getConfig('dateFormat:scaled'); - const { dateFormat } = this.props; + scaledDataFormat = this.props.getConfig('dateFormat:scaled'); + dateFormat = this.props.getConfig('dateFormat'); - if (!scaledDataFormat || !dateFormat) { + xAxisFormatter = (interval) => (val) => { + if (!this.scaledDataFormat || !this.dateFormat) { return val; } - const formatter = createXaxisFormatter(interval, scaledDataFormat, dateFormat); - + const formatter = createXaxisFormatter(interval, this.scaledDataFormat, this.dateFormat); return formatter(val); }; From 17d986e499f9877c28063fce1e941e25e04b7024 Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Thu, 3 Dec 2020 10:46:51 +0100 Subject: [PATCH 35/40] [Embeddable] Export CSV action for Lens embeddables in dashboard (#83654) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- ...ublic.uiactionsservice.addtriggeraction.md | 2 +- ...tions-public.uiactionsservice.getaction.md | 2 +- ...blic.uiactionsservice.gettriggeractions.md | 2 +- ...ionsservice.gettriggercompatibleactions.md | 2 +- ...gins-ui_actions-public.uiactionsservice.md | 10 +- ...-public.uiactionsservice.registeraction.md | 2 +- .../download-underlying-data.asciidoc | 15 ++ .../dashboard/explore-dashboard-data.asciidoc | 1 + .../images/download_csv_context_menu.png | Bin 0 -> 153535 bytes src/plugins/dashboard/kibana.json | 5 +- .../actions/export_csv_action.test.tsx | 134 +++++++++++++++++ .../application/actions/export_csv_action.tsx | 138 ++++++++++++++++++ .../public/application/actions/index.ts | 1 + src/plugins/dashboard/public/plugin.tsx | 13 +- .../data/common/exports/export_csv.tsx | 2 +- .../contact_card_exportable_embeddable.tsx | 44 ++++++ ...act_card_exportable_embeddable_factory.tsx | 85 +++++++++++ .../embeddables/contact_card/index.ts | 2 + src/plugins/ui_actions/public/public.api.md | 10 +- .../embeddable/embeddable.tsx | 14 ++ .../embeddable/expression_wrapper.tsx | 4 + x-pack/plugins/lens/public/plugin.ts | 18 ++- x-pack/test/functional/apps/lens/dashboard.ts | 15 ++ 23 files changed, 495 insertions(+), 26 deletions(-) create mode 100644 docs/user/dashboard/download-underlying-data.asciidoc create mode 100644 docs/user/dashboard/images/download_csv_context_menu.png create mode 100644 src/plugins/dashboard/public/application/actions/export_csv_action.test.tsx create mode 100644 src/plugins/dashboard/public/application/actions/export_csv_action.tsx create mode 100644 src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/contact_card_exportable_embeddable.tsx create mode 100644 src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/contact_card_exportable_embeddable_factory.tsx diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.addtriggeraction.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.addtriggeraction.md index 5a1ab83551d34..fd6ade88479af 100644 --- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.addtriggeraction.md +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.addtriggeraction.md @@ -11,5 +11,5 @@ Signature: ```typescript -readonly addTriggerAction: (triggerId: T, action: ActionDefinition | Action) => void; +readonly addTriggerAction: (triggerId: T, action: ActionDefinition | Action) => void; ``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.getaction.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.getaction.md index 5b0b3eea01cb1..d540de7637441 100644 --- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.getaction.md +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.getaction.md @@ -7,5 +7,5 @@ Signature: ```typescript -readonly getAction: >(id: string) => Action, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION">; +readonly getAction: >(id: string) => Action, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_EXPORT_CSV">; ``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggeractions.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggeractions.md index 2dda422046318..0a9b674a45de2 100644 --- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggeractions.md +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggeractions.md @@ -7,5 +7,5 @@ Signature: ```typescript -readonly getTriggerActions: (triggerId: T) => Action[]; +readonly getTriggerActions: (triggerId: T) => Action[]; ``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggercompatibleactions.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggercompatibleactions.md index e087753726a8a..faed81236342d 100644 --- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggercompatibleactions.md +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggercompatibleactions.md @@ -7,5 +7,5 @@ Signature: ```typescript -readonly getTriggerCompatibleActions: (triggerId: T, context: TriggerContextMapping[T]) => Promise[]>; +readonly getTriggerCompatibleActions: (triggerId: T, context: TriggerContextMapping[T]) => Promise[]>; ``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.md index f9eb693b492f7..e3c5dbb92ae90 100644 --- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.md +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.md @@ -21,19 +21,19 @@ export declare class UiActionsService | Property | Modifiers | Type | Description | | --- | --- | --- | --- | | [actions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.actions.md) | | ActionRegistry | | -| [addTriggerAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.addtriggeraction.md) | | <T extends "" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER" | "FILTER_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER">(triggerId: T, action: ActionDefinition<TriggerContextMapping[T]> | Action<TriggerContextMapping[T], "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION">) => void | addTriggerAction is similar to attachAction as it attaches action to a trigger, but it also registers the action, if it has not been registered, yet.addTriggerAction also infers better typing of the action argument. | +| [addTriggerAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.addtriggeraction.md) | | <T extends "" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER" | "FILTER_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER">(triggerId: T, action: ActionDefinition<TriggerContextMapping[T]> | Action<TriggerContextMapping[T], "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_EXPORT_CSV">) => void | addTriggerAction is similar to attachAction as it attaches action to a trigger, but it also registers the action, if it has not been registered, yet.addTriggerAction also infers better typing of the action argument. | | [attachAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.attachaction.md) | | <T extends "" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER" | "FILTER_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER">(triggerId: T, actionId: string) => void | | | [clear](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.clear.md) | | () => void | Removes all registered triggers and actions. | | [detachAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.detachaction.md) | | (triggerId: TriggerId, actionId: string) => void | | | [executeTriggerActions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.executetriggeractions.md) | | <T extends "" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER" | "FILTER_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER">(triggerId: T, context: TriggerContext<T>) => Promise<void> | | | [executionService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.executionservice.md) | | UiActionsExecutionService | | | [fork](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.fork.md) | | () => UiActionsService | "Fork" a separate instance of UiActionsService that inherits all existing triggers and actions, but going forward all new triggers and actions added to this instance of UiActionsService are only available within this instance. | -| [getAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.getaction.md) | | <T extends ActionDefinition<{}>>(id: string) => Action<ActionContext<T>, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION"> | | +| [getAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.getaction.md) | | <T extends ActionDefinition<{}>>(id: string) => Action<ActionContext<T>, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_EXPORT_CSV"> | | | [getTrigger](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettrigger.md) | | <T extends "" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER" | "FILTER_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER">(triggerId: T) => TriggerContract<T> | | -| [getTriggerActions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggeractions.md) | | <T extends "" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER" | "FILTER_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER">(triggerId: T) => Action<TriggerContextMapping[T], "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION">[] | | -| [getTriggerCompatibleActions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggercompatibleactions.md) | | <T extends "" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER" | "FILTER_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER">(triggerId: T, context: TriggerContextMapping[T]) => Promise<Action<TriggerContextMapping[T], "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION">[]> | | +| [getTriggerActions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggeractions.md) | | <T extends "" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER" | "FILTER_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER">(triggerId: T) => Action<TriggerContextMapping[T], "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_EXPORT_CSV">[] | | +| [getTriggerCompatibleActions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggercompatibleactions.md) | | <T extends "" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER" | "FILTER_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER">(triggerId: T, context: TriggerContextMapping[T]) => Promise<Action<TriggerContextMapping[T], "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_EXPORT_CSV">[]> | | | [hasAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.hasaction.md) | | (actionId: string) => boolean | | -| [registerAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.registeraction.md) | | <A extends ActionDefinition<{}>>(definition: A) => Action<ActionContext<A>, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION"> | | +| [registerAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.registeraction.md) | | <A extends ActionDefinition<{}>>(definition: A) => Action<ActionContext<A>, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_EXPORT_CSV"> | | | [registerTrigger](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.registertrigger.md) | | (trigger: Trigger) => void | | | [triggers](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.triggers.md) | | TriggerRegistry | | | [triggerToActions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.triggertoactions.md) | | TriggerToActionsRegistry | | diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.registeraction.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.registeraction.md index bd340eb76fbac..6f03777e14552 100644 --- a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.registeraction.md +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.registeraction.md @@ -7,5 +7,5 @@ Signature: ```typescript -readonly registerAction:
    >(definition: A) => Action, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION">; +readonly registerAction: >(definition: A) => Action, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_EXPORT_CSV">; ``` diff --git a/docs/user/dashboard/download-underlying-data.asciidoc b/docs/user/dashboard/download-underlying-data.asciidoc new file mode 100644 index 0000000000000..78403ba797d78 --- /dev/null +++ b/docs/user/dashboard/download-underlying-data.asciidoc @@ -0,0 +1,15 @@ +[float] +[role="xpack"] +[[download_csv]] +=== Download CSV + +To download the underlying data of the Lens panels on your dashboard, you can use the *Download as CSV* option. + +TIP: The *Download as CSV* option supports multiple CSV file downloads from the same Lens visualization out of the box, if configured: for instance with multiple layers on a bar chart. + +To use the *Download as CSV* option: + +* Click the from the panel menu, then click *Download as CSV*. ++ +[role="screenshot"] +image::images/download_csv_context_menu.png[Download as CSV from panel context menu] \ No newline at end of file diff --git a/docs/user/dashboard/explore-dashboard-data.asciidoc b/docs/user/dashboard/explore-dashboard-data.asciidoc index 238dfb79e900b..66f91dc2bc18c 100644 --- a/docs/user/dashboard/explore-dashboard-data.asciidoc +++ b/docs/user/dashboard/explore-dashboard-data.asciidoc @@ -16,3 +16,4 @@ The data that displays depends on the element that you inspect. image:images/Dashboard_inspect.png[Inspect in dashboard] include::explore-underlying-data.asciidoc[] +include::download-underlying-data.asciidoc[] diff --git a/docs/user/dashboard/images/download_csv_context_menu.png b/docs/user/dashboard/images/download_csv_context_menu.png new file mode 100644 index 0000000000000000000000000000000000000000..09f82b781249579c7f5ee92a111059112ad3b4d1 GIT binary patch literal 153535 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 { + const { setup, doStart } = embeddablePluginMock.createInstance(); + setup.registerEmbeddableFactory( + CONTACT_CARD_EXPORTABLE_EMBEDDABLE, + new ContactCardExportableEmbeddableFactory((() => null) as any, {} as any) + ); + const start = doStart(); + + let container: DashboardContainer; + let embeddable: ContactCardEmbeddable; + let coreStart: CoreStart; + let dataMock: jest.Mocked; + + beforeEach(async () => { + coreStart = coreMock.createStart(); + coreStart.savedObjects.client = { + ...coreStart.savedObjects.client, + get: jest.fn().mockImplementation(() => ({ attributes: { title: 'Holy moly' } })), + find: jest.fn().mockImplementation(() => ({ total: 15 })), + create: jest.fn().mockImplementation(() => ({ id: 'brandNewSavedObject' })), + }; + + const options = { + ExitFullScreenButton: () => null, + SavedObjectFinder: () => null, + application: {} as any, + embeddable: start, + inspector: {} as any, + notifications: {} as any, + overlays: coreStart.overlays, + savedObjectMetaData: {} as any, + uiActions: {} as any, + }; + const input = getSampleDashboardInput({ + panels: { + '123': getSampleDashboardPanel({ + explicitInput: { firstName: 'Kibanana', id: '123' }, + type: CONTACT_CARD_EXPORTABLE_EMBEDDABLE, + }), + }, + }); + container = new DashboardContainer(input, options); + dataMock = dataPluginMock.createStartContract(); + + const contactCardEmbeddable = await container.addNewEmbeddable< + ContactCardEmbeddableInput, + ContactCardEmbeddableOutput, + ContactCardEmbeddable + >(CONTACT_CARD_EXPORTABLE_EMBEDDABLE, { + firstName: 'Kibana', + }); + + if (isErrorEmbeddable(contactCardEmbeddable)) { + throw new Error('Failed to create embeddable'); + } else { + embeddable = contactCardEmbeddable; + } + }); + + test('Download is incompatible with embeddables without getInspectorAdapters implementation', async () => { + const action = new ExportCSVAction({ core: coreStart, data: dataMock }); + const errorEmbeddable = new ErrorEmbeddable( + 'Wow what an awful error', + { id: ' 404' }, + embeddable.getRoot() as IContainer + ); + expect(await action.isCompatible({ embeddable: errorEmbeddable })).toBe(false); + }); + + test('Should download a compatible Embeddable', async () => { + const action = new ExportCSVAction({ core: coreStart, data: dataMock }); + const result = ((await action.execute({ embeddable, asString: true })) as unknown) as + | undefined + | Record; + expect(result).toEqual({ + 'Hello Kibana.csv': { + content: `First Name,Last Name${LINE_FEED_CHARACTER}Kibana,undefined${LINE_FEED_CHARACTER}`, + type: 'text/plain;charset=utf-8', + }, + }); + }); + + test('Should not download incompatible Embeddable', async () => { + const action = new ExportCSVAction({ core: coreStart, data: dataMock }); + const errorEmbeddable = new ErrorEmbeddable( + 'Wow what an awful error', + { id: ' 404' }, + embeddable.getRoot() as IContainer + ); + const result = ((await action.execute({ + embeddable: errorEmbeddable, + asString: true, + })) as unknown) as undefined | Record; + expect(result).toBeUndefined(); + }); +}); diff --git a/src/plugins/dashboard/public/application/actions/export_csv_action.tsx b/src/plugins/dashboard/public/application/actions/export_csv_action.tsx new file mode 100644 index 0000000000000..48a7877f9383e --- /dev/null +++ b/src/plugins/dashboard/public/application/actions/export_csv_action.tsx @@ -0,0 +1,138 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { Datatable } from 'src/plugins/expressions/public'; +import { FormatFactory } from '../../../../data/common/field_formats/utils'; +import { DataPublicPluginStart, exporters } from '../../../../data/public'; +import { downloadMultipleAs } from '../../../../share/public'; +import { Adapters, IEmbeddable } from '../../../../embeddable/public'; +import { ActionByType } from '../../../../ui_actions/public'; +import { CoreStart } from '../../../../../core/public'; + +export const ACTION_EXPORT_CSV = 'ACTION_EXPORT_CSV'; + +export interface Params { + core: CoreStart; + data: DataPublicPluginStart; +} + +export interface ExportContext { + embeddable?: IEmbeddable; + // used for testing + asString?: boolean; +} + +/** + * This is "Export CSV" action which appears in the context + * menu of a dashboard panel. + */ +export class ExportCSVAction implements ActionByType { + public readonly id = ACTION_EXPORT_CSV; + + public readonly type = ACTION_EXPORT_CSV; + + public readonly order = 5; + + constructor(protected readonly params: Params) {} + + public getIconType() { + return 'exportAction'; + } + + public readonly getDisplayName = (context: ExportContext): string => + i18n.translate('dashboard.actions.DownloadCreateDrilldownAction.displayName', { + defaultMessage: 'Download as CSV', + }); + + public async isCompatible(context: ExportContext): Promise { + return !!this.hasDatatableContent(context.embeddable?.getInspectorAdapters?.()); + } + + private hasDatatableContent = (adapters: Adapters | undefined) => { + return Object.keys(adapters?.tables || {}).length > 0; + }; + + private getFormatter = (): FormatFactory | undefined => { + if (this.params.data) { + return this.params.data.fieldFormats.deserialize; + } + }; + + private getDataTableContent = (adapters: Adapters | undefined) => { + if (this.hasDatatableContent(adapters)) { + return adapters?.tables; + } + return; + }; + + private exportCSV = async (context: ExportContext) => { + const formatFactory = this.getFormatter(); + // early exit if not formatter is available + if (!formatFactory) { + return; + } + const tableAdapters = this.getDataTableContent( + context?.embeddable?.getInspectorAdapters() + ) as Record; + + if (tableAdapters) { + const datatables = Object.values(tableAdapters); + const content = datatables.reduce>( + (memo, datatable, i) => { + // skip empty datatables + if (datatable) { + const postFix = datatables.length > 1 ? `-${i + 1}` : ''; + const untitledFilename = i18n.translate( + 'dashboard.actions.downloadOptionsUnsavedFilename', + { + defaultMessage: 'unsaved', + } + ); + + memo[`${context!.embeddable!.getTitle() || untitledFilename}${postFix}.csv`] = { + content: exporters.datatableToCSV(datatable, { + csvSeparator: this.params.core.uiSettings.get('csv:separator', ','), + quoteValues: this.params.core.uiSettings.get('csv:quoteValues', true), + formatFactory, + }), + type: exporters.CSV_MIME_TYPE, + }; + } + return memo; + }, + {} + ); + + // useful for testing + if (context.asString) { + return (content as unknown) as Promise; + } + + if (content) { + return downloadMultipleAs(content); + } + } + }; + + public async execute(context: ExportContext): Promise { + // make it testable: type here will be forced + return await this.exportCSV(context); + } +} diff --git a/src/plugins/dashboard/public/application/actions/index.ts b/src/plugins/dashboard/public/application/actions/index.ts index cd32c2025456f..3d7ebe76cb66a 100644 --- a/src/plugins/dashboard/public/application/actions/index.ts +++ b/src/plugins/dashboard/public/application/actions/index.ts @@ -47,3 +47,4 @@ export { LibraryNotificationAction, ACTION_LIBRARY_NOTIFICATION, } from './library_notification_action'; +export { ExportContext, ExportCSVAction, ACTION_EXPORT_CSV } from './export_csv_action'; diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index c47a4c2d21b11..76b1ccc037e89 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -101,6 +101,11 @@ import { DashboardConstants } from './dashboard_constants'; import { addEmbeddableToDashboardUrl } from './url_utils/url_helper'; import { PlaceholderEmbeddableFactory } from './application/embeddable/placeholder'; import { UrlGeneratorState } from '../../share/public'; +import { + ACTION_EXPORT_CSV, + ExportContext, + ExportCSVAction, +} from './application/actions/export_csv_action'; declare module '../../share/public' { export interface UrlGeneratorStateMapping { @@ -160,6 +165,7 @@ declare module '../../../plugins/ui_actions/public' { [ACTION_ADD_TO_LIBRARY]: AddToLibraryActionContext; [ACTION_UNLINK_FROM_LIBRARY]: UnlinkFromLibraryActionContext; [ACTION_LIBRARY_NOTIFICATION]: LibraryNotificationActionContext; + [ACTION_EXPORT_CSV]: ExportContext; } } @@ -414,7 +420,7 @@ export class DashboardPlugin public start(core: CoreStart, plugins: StartDependencies): DashboardStart { const { notifications } = core; - const { uiActions } = plugins; + const { uiActions, data, share } = plugins; const SavedObjectFinder = getSavedObjectFinder(core.savedObjects, core.uiSettings); @@ -431,6 +437,11 @@ export class DashboardPlugin uiActions.registerAction(clonePanelAction); uiActions.attachAction(CONTEXT_MENU_TRIGGER, clonePanelAction.id); + if (share) { + const ExportCSVPlugin = new ExportCSVAction({ core, data }); + uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, ExportCSVPlugin); + } + if (this.dashboardFeatureFlagConfig?.allowByValueEmbeddables) { const addToLibraryAction = new AddToLibraryAction({ toasts: notifications.toasts }); uiActions.registerAction(addToLibraryAction); diff --git a/src/plugins/data/common/exports/export_csv.tsx b/src/plugins/data/common/exports/export_csv.tsx index 1e1420c245eb4..116586c5b66e8 100644 --- a/src/plugins/data/common/exports/export_csv.tsx +++ b/src/plugins/data/common/exports/export_csv.tsx @@ -22,7 +22,7 @@ import { FormatFactory } from 'src/plugins/data/common/field_formats/utils'; import { Datatable } from 'src/plugins/expressions'; -const LINE_FEED_CHARACTER = '\r\n'; +export const LINE_FEED_CHARACTER = '\r\n'; const nonAlphaNumRE = /[^a-zA-Z0-9]/; const allDoubleQuoteRE = /"/g; export const CSV_MIME_TYPE = 'text/plain;charset=utf-8'; diff --git a/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/contact_card_exportable_embeddable.tsx b/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/contact_card_exportable_embeddable.tsx new file mode 100644 index 0000000000000..338eb4877a50a --- /dev/null +++ b/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/contact_card_exportable_embeddable.tsx @@ -0,0 +1,44 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ContactCardEmbeddable } from './contact_card_embeddable'; + +export class ContactCardExportableEmbeddable extends ContactCardEmbeddable { + public getInspectorAdapters = () => { + return { + tables: { + layer1: { + type: 'datatable', + columns: [ + { id: 'firstName', name: 'First Name' }, + { id: 'originalLastName', name: 'Last Name' }, + ], + rows: [ + { + firstName: this.getInput().firstName, + orignialLastName: this.getInput().lastName, + }, + ], + }, + }, + }; + }; +} + +export const CONTACT_EXPORTABLE_USER_TRIGGER = 'CONTACT_EXPORTABLE_USER_TRIGGER'; diff --git a/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/contact_card_exportable_embeddable_factory.tsx b/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/contact_card_exportable_embeddable_factory.tsx new file mode 100644 index 0000000000000..5b8827ac6fc2a --- /dev/null +++ b/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/contact_card_exportable_embeddable_factory.tsx @@ -0,0 +1,85 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { UiActionsStart } from 'src/plugins/ui_actions/public'; + +import { CoreStart } from 'src/core/public'; +import { toMountPoint } from '../../../../../../kibana_react/public'; +import { EmbeddableFactoryDefinition } from '../../../embeddables'; +import { Container } from '../../../containers'; +import { ContactCardEmbeddableInput } from './contact_card_embeddable'; +import { ContactCardExportableEmbeddable } from './contact_card_exportable_embeddable'; +import { ContactCardInitializer } from './contact_card_initializer'; + +export const CONTACT_CARD_EXPORTABLE_EMBEDDABLE = 'CONTACT_CARD_EXPORTABLE_EMBEDDABLE'; + +export class ContactCardExportableEmbeddableFactory + implements EmbeddableFactoryDefinition { + public readonly type = CONTACT_CARD_EXPORTABLE_EMBEDDABLE; + + constructor( + private readonly execTrigger: UiActionsStart['executeTriggerActions'], + private readonly overlays: CoreStart['overlays'] + ) {} + + public async isEditable() { + return true; + } + + public getDisplayName() { + return i18n.translate('embeddableApi.samples.contactCard.displayName', { + defaultMessage: 'contact card', + }); + } + + public getExplicitInput = (): Promise> => { + return new Promise((resolve) => { + const modalSession = this.overlays.openModal( + toMountPoint( + { + modalSession.close(); + // @ts-expect-error + resolve(undefined); + }} + onCreate={(input: { firstName: string; lastName?: string }) => { + modalSession.close(); + resolve(input); + }} + /> + ), + { + 'data-test-subj': 'createContactCardEmbeddable', + } + ); + }); + }; + + public create = async (initialInput: ContactCardEmbeddableInput, parent?: Container) => { + return new ContactCardExportableEmbeddable( + initialInput, + { + execAction: this.execTrigger, + }, + parent + ); + }; +} diff --git a/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/index.ts b/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/index.ts index c79a4f517916e..a9006cdc7b477 100644 --- a/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/index.ts +++ b/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/index.ts @@ -20,5 +20,7 @@ export * from './contact_card'; export * from './contact_card_embeddable'; export * from './contact_card_embeddable_factory'; +export * from './contact_card_exportable_embeddable'; +export * from './contact_card_exportable_embeddable_factory'; export * from './contact_card_initializer'; export * from './slow_contact_card_embeddable_factory'; diff --git a/src/plugins/ui_actions/public/public.api.md b/src/plugins/ui_actions/public/public.api.md index 3a14f49169e09..ca27e19b247c2 100644 --- a/src/plugins/ui_actions/public/public.api.md +++ b/src/plugins/ui_actions/public/public.api.md @@ -234,7 +234,7 @@ export class UiActionsService { // // (undocumented) protected readonly actions: ActionRegistry; - readonly addTriggerAction: (triggerId: T, action: UiActionsActionDefinition | Action) => void; + readonly addTriggerAction: (triggerId: T, action: UiActionsActionDefinition | Action) => void; // (undocumented) readonly attachAction: (triggerId: T, actionId: string) => void; readonly clear: () => void; @@ -248,21 +248,21 @@ export class UiActionsService { readonly executionService: UiActionsExecutionService; readonly fork: () => UiActionsService; // (undocumented) - readonly getAction: >(id: string) => Action, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION">; + readonly getAction: >(id: string) => Action, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_EXPORT_CSV">; // Warning: (ae-forgotten-export) The symbol "TriggerContract" needs to be exported by the entry point index.d.ts // // (undocumented) readonly getTrigger: (triggerId: T) => TriggerContract; // (undocumented) - readonly getTriggerActions: (triggerId: T) => Action[]; + readonly getTriggerActions: (triggerId: T) => Action[]; // (undocumented) - readonly getTriggerCompatibleActions: (triggerId: T, context: TriggerContextMapping[T]) => Promise[]>; + readonly getTriggerCompatibleActions: (triggerId: T, context: TriggerContextMapping[T]) => Promise[]>; // (undocumented) readonly hasAction: (actionId: string) => boolean; // Warning: (ae-forgotten-export) The symbol "ActionContext" needs to be exported by the entry point index.d.ts // // (undocumented) - readonly registerAction: >(definition: A) => Action, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION">; + readonly registerAction: >(definition: A) => Action, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_VISUALIZE_LENS_FIELD" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_EXPORT_CSV">; // (undocumented) readonly registerTrigger: (trigger: Trigger) => void; // Warning: (ae-forgotten-export) The symbol "TriggerRegistry" needs to be exported by the entry point index.d.ts diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx index 76276f8b4c828..56d471be63d3e 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx @@ -44,6 +44,7 @@ import { IndexPatternsContract } from '../../../../../../src/plugins/data/public import { getEditPath, DOC_TYPE } from '../../../common'; import { IBasePath } from '../../../../../../src/core/public'; import { LensAttributeService } from '../../lens_attribute_service'; +import { LensInspectorAdapters } from '../types'; export type LensSavedObjectAttributes = Omit; @@ -84,6 +85,7 @@ export class Embeddable private subscription: Subscription; private autoRefreshFetchSubscription: Subscription; private isInitialized = false; + private activeData: LensInspectorAdapters | undefined; private externalSearchContext: { timeRange?: TimeRange; @@ -131,6 +133,10 @@ export class Embeddable } } + public getInspectorAdapters() { + return this.activeData; + } + async initializeSavedVis(input: LensEmbeddableInput) { const attributes: | LensSavedObjectAttributes @@ -175,6 +181,13 @@ export class Embeddable } } + private updateActiveData = ( + data: unknown, + inspectorAdapters?: LensInspectorAdapters | undefined + ) => { + this.activeData = inspectorAdapters; + }; + /** * * @param {HTMLElement} domNode @@ -194,6 +207,7 @@ export class Embeddable variables={input.palette ? { theme: { palette: input.palette } } : {}} searchSessionId={this.input.searchSessionId} handleEvent={this.handleEvent} + onData$={this.updateActiveData} renderMode={input.renderMode} />, domNode diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx index d18372246b0e6..4645420898314 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx @@ -15,6 +15,7 @@ import { import { ExecutionContextSearch } from 'src/plugins/data/public'; import { RenderMode } from 'src/plugins/expressions'; import { getOriginalRequestErrorMessage } from '../error_helper'; +import { LensInspectorAdapters } from '../types'; export interface ExpressionWrapperProps { ExpressionRenderer: ReactExpressionRendererType; @@ -23,6 +24,7 @@ export interface ExpressionWrapperProps { searchContext: ExecutionContextSearch; searchSessionId?: string; handleEvent: (event: ExpressionRendererEvent) => void; + onData$: (data: unknown, inspectorAdapters?: LensInspectorAdapters | undefined) => void; renderMode?: RenderMode; } @@ -33,6 +35,7 @@ export function ExpressionWrapper({ variables, handleEvent, searchSessionId, + onData$, renderMode, }: ExpressionWrapperProps) { return ( @@ -60,6 +63,7 @@ export function ExpressionWrapper({ expression={expression} searchContext={searchContext} searchSessionId={searchSessionId} + onData$={onData$} renderMode={renderMode} renderError={(errorMessage, error) => (

    diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index 2f9310ee24ae9..24075facb68eb 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -5,13 +5,16 @@ */ import { AppMountParameters, CoreSetup, CoreStart } from 'kibana/public'; -import { DataPublicPluginSetup, DataPublicPluginStart } from 'src/plugins/data/public'; -import { EmbeddableSetup, EmbeddableStart } from 'src/plugins/embeddable/public'; -import { DashboardStart } from 'src/plugins/dashboard/public'; -import { ExpressionsSetup, ExpressionsStart } from 'src/plugins/expressions/public'; -import { VisualizationsSetup, VisualizationsStart } from 'src/plugins/visualizations/public'; -import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; -import { UrlForwardingSetup } from 'src/plugins/url_forwarding/public'; +import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../../src/plugins/data/public'; +import { EmbeddableSetup, EmbeddableStart } from '../../../../src/plugins/embeddable/public'; +import { DashboardStart } from '../../../../src/plugins/dashboard/public'; +import { ExpressionsSetup, ExpressionsStart } from '../../../../src/plugins/expressions/public'; +import { + VisualizationsSetup, + VisualizationsStart, +} from '../../../../src/plugins/visualizations/public'; +import { NavigationPublicPluginStart } from '../../../../src/plugins/navigation/public'; +import { UrlForwardingSetup } from '../../../../src/plugins/url_forwarding/public'; import { GlobalSearchPluginSetup } from '../../global_search/public'; import { ChartsPluginSetup, ChartsPluginStart } from '../../../../src/plugins/charts/public'; import { EditorFrameService } from './editor_frame_service'; @@ -64,6 +67,7 @@ export interface LensPluginStartDependencies { charts: ChartsPluginStart; savedObjectsTagging?: SavedObjectTaggingPluginStart; } + export class LensPlugin { private datatableVisualization: DatatableVisualization; private editorFrameService: EditorFrameService; diff --git a/x-pack/test/functional/apps/lens/dashboard.ts b/x-pack/test/functional/apps/lens/dashboard.ts index 17b70b8510f04..c332d05039255 100644 --- a/x-pack/test/functional/apps/lens/dashboard.ts +++ b/x-pack/test/functional/apps/lens/dashboard.ts @@ -140,5 +140,20 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const hasGeoSrcFilter = await filterBar.hasFilter('geo.src', 'US', true, true); expect(hasGeoSrcFilter).to.be(true); }); + + it('CSV export action exists in panel context menu', async () => { + const ACTION_ID = 'ACTION_EXPORT_CSV'; + const ACTION_TEST_SUBJ = `embeddablePanelAction-${ACTION_ID}`; + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.clickNewDashboard(); + await dashboardAddPanel.clickOpenAddPanel(); + await dashboardAddPanel.filterEmbeddableNames('lnsPieVis'); + await find.clickByButtonText('lnsPieVis'); + await dashboardAddPanel.closeAddPanel(); + + await panelActions.openContextMenu(); + await panelActions.clickContextMenuMoreItem(); + await testSubjects.existOrFail(ACTION_TEST_SUBJ); + }); }); } From ae463cf1d7dfdcc0f8f0454a03e0e18e3f777a97 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 3 Dec 2020 11:13:55 +0100 Subject: [PATCH 36/40] [Discover] Refactor getContextUrl to separate file (#84503) --- .../angular/doc_table/components/table_row.ts | 32 +++--------- .../helpers/get_context_url.test.ts | 41 +++++++++++++++ .../application/helpers/get_context_url.tsx | 52 +++++++++++++++++++ 3 files changed, 100 insertions(+), 25 deletions(-) create mode 100644 src/plugins/discover/public/application/helpers/get_context_url.test.ts create mode 100644 src/plugins/discover/public/application/helpers/get_context_url.tsx diff --git a/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts b/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts index 17f3199b75b15..e45f18606e3fc 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts +++ b/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts @@ -18,20 +18,15 @@ */ import { find, template } from 'lodash'; -import { stringify } from 'query-string'; import $ from 'jquery'; -import rison from 'rison-node'; -import '../../doc_viewer'; - import openRowHtml from './table_row/open.html'; import detailsHtml from './table_row/details.html'; - -import { dispatchRenderComplete, url } from '../../../../../../kibana_utils/public'; +import { dispatchRenderComplete } from '../../../../../../kibana_utils/public'; import { DOC_HIDE_TIME_COLUMN_SETTING } from '../../../../../common'; import cellTemplateHtml from '../components/table_row/cell.html'; import truncateByHeightTemplateHtml from '../components/table_row/truncate_by_height.html'; -import { esFilters } from '../../../../../../data/public'; import { getServices } from '../../../../kibana_services'; +import { getContextUrl } from '../../../helpers/get_context_url'; const TAGS_WITH_WS = />\s+ { - const globalFilters: any = getServices().filterManager.getGlobalFilters(); - const appFilters: any = getServices().filterManager.getAppFilters(); - - const hash = stringify( - url.encodeQuery({ - _g: rison.encode({ - filters: globalFilters || [], - }), - _a: rison.encode({ - columns: $scope.columns, - filters: (appFilters || []).map(esFilters.disableFilter), - }), - }), - { encode: false, sort: false } + return getContextUrl( + $scope.row._id, + $scope.indexPattern.id, + $scope.columns, + getServices().filterManager ); - - return `#/context/${encodeURIComponent($scope.indexPattern.id)}/${encodeURIComponent( - $scope.row._id - )}?${hash}`; }; // create a tr element that lists the value for each *column* diff --git a/src/plugins/discover/public/application/helpers/get_context_url.test.ts b/src/plugins/discover/public/application/helpers/get_context_url.test.ts new file mode 100644 index 0000000000000..481ea6b1a5b4f --- /dev/null +++ b/src/plugins/discover/public/application/helpers/get_context_url.test.ts @@ -0,0 +1,41 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { getContextUrl } from './get_context_url'; +import { FilterManager } from '../../../../data/public/query/filter_manager'; +const filterManager = ({ + getGlobalFilters: () => [], + getAppFilters: () => [], +} as unknown) as FilterManager; + +describe('Get context url', () => { + test('returning a valid context url', async () => { + const url = await getContextUrl('docId', 'ipId', ['test1', 'test2'], filterManager); + expect(url).toMatchInlineSnapshot( + `"#/context/ipId/docId?_g=(filters:!())&_a=(columns:!(test1,test2),filters:!())"` + ); + }); + + test('returning a valid context url when docId contains whitespace', async () => { + const url = await getContextUrl('doc Id', 'ipId', ['test1', 'test2'], filterManager); + expect(url).toMatchInlineSnapshot( + `"#/context/ipId/doc%20Id?_g=(filters:!())&_a=(columns:!(test1,test2),filters:!())"` + ); + }); +}); diff --git a/src/plugins/discover/public/application/helpers/get_context_url.tsx b/src/plugins/discover/public/application/helpers/get_context_url.tsx new file mode 100644 index 0000000000000..b159341cbe28d --- /dev/null +++ b/src/plugins/discover/public/application/helpers/get_context_url.tsx @@ -0,0 +1,52 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { stringify } from 'query-string'; +import rison from 'rison-node'; +import { url } from '../../../../kibana_utils/common'; +import { esFilters, FilterManager } from '../../../../data/public'; + +/** + * Helper function to generate an URL to a document in Discover's context view + */ +export function getContextUrl( + documentId: string, + indexPatternId: string, + columns: string[], + filterManager: FilterManager +) { + const globalFilters = filterManager.getGlobalFilters(); + const appFilters = filterManager.getAppFilters(); + + const hash = stringify( + url.encodeQuery({ + _g: rison.encode({ + filters: globalFilters || [], + }), + _a: rison.encode({ + columns, + filters: (appFilters || []).map(esFilters.disableFilter), + }), + }), + { encode: false, sort: false } + ); + + return `#/context/${encodeURIComponent(indexPatternId)}/${encodeURIComponent( + documentId + )}?${hash}`; +} From bec33426ebddfd9e3f393247f13afd2a190db120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yulia=20=C4=8Cech?= <6585477+yuliacech@users.noreply.github.com> Date: Thu, 3 Dec 2020 11:21:04 +0100 Subject: [PATCH 37/40] Fixed a11y issue on rollup jobs table selection (#84567) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../crud_app/sections/job_list/job_table/job_table.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/x-pack/plugins/rollup/public/crud_app/sections/job_list/job_table/job_table.js b/x-pack/plugins/rollup/public/crud_app/sections/job_list/job_table/job_table.js index 66ecb37d68439..0fd7f62511bdb 100644 --- a/x-pack/plugins/rollup/public/crud_app/sections/job_list/job_table/job_table.js +++ b/x-pack/plugins/rollup/public/crud_app/sections/job_list/job_table/job_table.js @@ -310,6 +310,10 @@ export class JobTable extends Component { this.toggleItem(id); }} data-test-subj={`indexTableRowCheckbox-${id}`} + aria-label={i18n.translate('xpack.rollupJobs.jobTable.selectRow', { + defaultMessage: 'Select this row {id}', + values: { id }, + })} /> @@ -380,6 +384,9 @@ export class JobTable extends Component { checked={this.areAllItemsSelected()} onChange={this.toggleAll} type="inList" + aria-label={i18n.translate('xpack.rollupJobs.jobTable.selectAllRows', { + defaultMessage: 'Select all rows', + })} /> {this.buildHeader()} From f5fb14f07aefd15c3577aba8e4023592f4edc8f8 Mon Sep 17 00:00:00 2001 From: eyalkoren <41850454+eyalkoren@users.noreply.github.com> Date: Thu, 3 Dec 2020 12:22:38 +0200 Subject: [PATCH 38/40] [APM] Add APM agent config options (#84678) --- .../runtime_types/log_level_rt.ts | 17 ++++++ .../__snapshots__/index.test.ts.snap | 44 ++++++++++++++- .../setting_definitions/general_settings.ts | 53 ++++++++++++++++++- .../setting_definitions/index.test.ts | 3 ++ 4 files changed, 113 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugins/apm/common/agent_configuration/runtime_types/log_level_rt.ts diff --git a/x-pack/plugins/apm/common/agent_configuration/runtime_types/log_level_rt.ts b/x-pack/plugins/apm/common/agent_configuration/runtime_types/log_level_rt.ts new file mode 100644 index 0000000000000..b488faa8e8fdc --- /dev/null +++ b/x-pack/plugins/apm/common/agent_configuration/runtime_types/log_level_rt.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as t from 'io-ts'; + +export const logLevelRt = t.union([ + t.literal('trace'), + t.literal('debug'), + t.literal('info'), + t.literal('warning'), + t.literal('error'), + t.literal('critical'), + t.literal('off'), +]); diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap index 2962a5fd2df3b..fc42af5ff7724 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap @@ -64,8 +64,38 @@ Array [ }, Object { "key": "log_level", - "type": "text", - "validationName": "string", + "options": Array [ + Object { + "text": "trace", + "value": "trace", + }, + Object { + "text": "debug", + "value": "debug", + }, + Object { + "text": "info", + "value": "info", + }, + Object { + "text": "warning", + "value": "warning", + }, + Object { + "text": "error", + "value": "error", + }, + Object { + "text": "critical", + "value": "critical", + }, + Object { + "text": "off", + "value": "off", + }, + ], + "type": "select", + "validationName": "(\\"trace\\" | \\"debug\\" | \\"info\\" | \\"warning\\" | \\"error\\" | \\"critical\\" | \\"off\\")", }, Object { "key": "profiling_inferred_spans_enabled", @@ -110,6 +140,11 @@ Array [ "type": "boolean", "validationName": "(\\"true\\" | \\"false\\")", }, + Object { + "key": "sanitize_field_names", + "type": "text", + "validationName": "string", + }, Object { "key": "server_timeout", "min": "1ms", @@ -170,6 +205,11 @@ Array [ "type": "float", "validationName": "floatRt", }, + Object { + "key": "transaction_ignore_urls", + "type": "text", + "validationName": "string", + }, Object { "key": "transaction_max_spans", "max": undefined, diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts index e777e1fd09d0b..59a315830aec5 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts @@ -6,6 +6,7 @@ import { i18n } from '@kbn/i18n'; import { captureBodyRt } from '../runtime_types/capture_body_rt'; +import { logLevelRt } from '../runtime_types/log_level_rt'; import { RawSettingDefinition } from './types'; export const generalSettings: RawSettingDefinition[] = [ @@ -91,7 +92,8 @@ export const generalSettings: RawSettingDefinition[] = [ // LOG_LEVEL { key: 'log_level', - type: 'text', + validation: logLevelRt, + type: 'select', defaultValue: 'info', label: i18n.translate('xpack.apm.agentConfig.logLevel.label', { defaultMessage: 'Log level', @@ -99,7 +101,16 @@ export const generalSettings: RawSettingDefinition[] = [ description: i18n.translate('xpack.apm.agentConfig.logLevel.description', { defaultMessage: 'Sets the logging level for the agent', }), - includeAgents: ['dotnet', 'ruby'], + options: [ + { text: 'trace', value: 'trace' }, + { text: 'debug', value: 'debug' }, + { text: 'info', value: 'info' }, + { text: 'warning', value: 'warning' }, + { text: 'error', value: 'error' }, + { text: 'critical', value: 'critical' }, + { text: 'off', value: 'off' }, + ], + includeAgents: ['dotnet', 'ruby', 'java'], }, // Recording @@ -207,4 +218,42 @@ export const generalSettings: RawSettingDefinition[] = [ } ), }, + + // Sanitize field names + { + key: 'sanitize_field_names', + type: 'text', + defaultValue: + 'password, passwd, pwd, secret, *key, *token*, *session*, *credit*, *card*, authorization, set-cookie', + label: i18n.translate('xpack.apm.agentConfig.sanitizeFiledNames.label', { + defaultMessage: 'Sanitize field names', + }), + description: i18n.translate( + 'xpack.apm.agentConfig.sanitizeFiledNames.description', + { + defaultMessage: + 'Sometimes it is necessary to sanitize, i.e., remove, sensitive data sent to Elastic APM. This config accepts a list of wildcard patterns of field names which should be sanitized. These apply to HTTP headers (including cookies) and `application/x-www-form-urlencoded` data (POST form fields). The query string and the captured request body (such as `application/json` data) will not get sanitized.', + } + ), + includeAgents: ['java'], + }, + + // Ignore transactions based on URLs + { + key: 'transaction_ignore_urls', + type: 'text', + defaultValue: + 'Agent specific - check out the documentation of this config option in the corresponding agent documentation.', + label: i18n.translate('xpack.apm.agentConfig.transactionIgnoreUrl.label', { + defaultMessage: 'Ignore transactions based on URLs', + }), + description: i18n.translate( + 'xpack.apm.agentConfig.transactionIgnoreUrl.description', + { + defaultMessage: + 'Used to restrict requests to certain URLs from being instrumented. This config accepts a comma-separated list of wildcard patterns of URL paths that should be ignored. When an incoming HTTP request is detected, its request path will be tested against each element in this list. For example, adding `/home/index` to this list would match and remove instrumentation from `http://localhost/home/index` as well as `http://whatever.com/home/index?value1=123`', + } + ), + includeAgents: ['java'], + }, ]; diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/index.test.ts b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/index.test.ts index 1f247813104ec..a00f1ab5bb4d1 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/index.test.ts +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/index.test.ts @@ -61,12 +61,14 @@ describe('filterByAgent', () => { 'capture_headers', 'circuit_breaker_enabled', 'enable_log_correlation', + 'log_level', 'profiling_inferred_spans_enabled', 'profiling_inferred_spans_excluded_classes', 'profiling_inferred_spans_included_classes', 'profiling_inferred_spans_min_duration', 'profiling_inferred_spans_sampling_interval', 'recording', + 'sanitize_field_names', 'server_timeout', 'span_frames_min_duration', 'stack_trace_limit', @@ -75,6 +77,7 @@ describe('filterByAgent', () => { 'stress_monitor_gc_stress_threshold', 'stress_monitor_system_cpu_relief_threshold', 'stress_monitor_system_cpu_stress_threshold', + 'transaction_ignore_urls', 'transaction_max_spans', 'transaction_sample_rate', ]); From 37e907078c503aeb3e642ec674f98d59aee52607 Mon Sep 17 00:00:00 2001 From: MadameSheema Date: Thu, 3 Dec 2020 11:48:40 +0100 Subject: [PATCH 39/40] [Security Solution][Detections] Implements indicator match rule cypress test (#84323) * implemnts indicator match rule cypress test * fixes merge issue * fixes type check issues * fixes mapping * simplifies data * fixes excpetions flakiness * fixes alerts test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../cypress/integration/alerts.spec.ts | 36 +- .../alerts_detection_exceptions.spec.ts | 29 +- ...ts_detection_rules_indicator_match.spec.ts | 197 + .../security_solution/cypress/objects/rule.ts | 53 +- .../cypress/screens/alerts.ts | 4 +- .../cypress/screens/create_new_rule.ts | 6 + .../cypress/screens/rule_details.ts | 6 + .../cypress/tasks/create_new_rule.ts | 71 +- .../es_archives/threat_data/data.json.gz | Bin 0 -> 1086 bytes .../es_archives/threat_data/mappings.json | 3577 +++++++++++++++++ .../es_archives/threat_indicator/data.json | 13 + .../threat_indicator/mappings.json | 30 + 12 files changed, 3954 insertions(+), 68 deletions(-) create mode 100644 x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_indicator_match.spec.ts create mode 100644 x-pack/test/security_solution_cypress/es_archives/threat_data/data.json.gz create mode 100644 x-pack/test/security_solution_cypress/es_archives/threat_data/mappings.json create mode 100644 x-pack/test/security_solution_cypress/es_archives/threat_indicator/data.json create mode 100644 x-pack/test/security_solution_cypress/es_archives/threat_indicator/mappings.json diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts index 8e3b30cddd121..0810babc9370b 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ import { - NUMBER_OF_ALERTS, + ALERTS, + ALERTS_COUNT, SELECTED_ALERTS, SHOWING_ALERTS, - ALERTS, TAKE_ACTION_POPOVER_BTN, } from '../screens/alerts'; @@ -45,7 +45,7 @@ describe('Alerts', () => { waitForAlertsPanelToBeLoaded(); waitForAlertsToBeLoaded(); - cy.get(NUMBER_OF_ALERTS) + cy.get(ALERTS_COUNT) .invoke('text') .then((numberOfAlerts) => { cy.get(SHOWING_ALERTS).should('have.text', `Showing ${numberOfAlerts} alerts`); @@ -64,10 +64,7 @@ describe('Alerts', () => { waitForAlerts(); const expectedNumberOfAlertsAfterClosing = +numberOfAlerts - numberOfAlertsToBeClosed; - cy.get(NUMBER_OF_ALERTS).should( - 'have.text', - expectedNumberOfAlertsAfterClosing.toString() - ); + cy.get(ALERTS_COUNT).should('have.text', expectedNumberOfAlertsAfterClosing.toString()); cy.get(SHOWING_ALERTS).should( 'have.text', @@ -77,7 +74,7 @@ describe('Alerts', () => { goToClosedAlerts(); waitForAlerts(); - cy.get(NUMBER_OF_ALERTS).should('have.text', numberOfAlertsToBeClosed.toString()); + cy.get(ALERTS_COUNT).should('have.text', numberOfAlertsToBeClosed.toString()); cy.get(SHOWING_ALERTS).should( 'have.text', `Showing ${numberOfAlertsToBeClosed.toString()} alerts` @@ -98,7 +95,7 @@ describe('Alerts', () => { waitForAlerts(); const expectedNumberOfClosedAlertsAfterOpened = 2; - cy.get(NUMBER_OF_ALERTS).should( + cy.get(ALERTS_COUNT).should( 'have.text', expectedNumberOfClosedAlertsAfterOpened.toString() ); @@ -128,7 +125,7 @@ describe('Alerts', () => { it('Closes one alert when more than one opened alerts are selected', () => { waitForAlertsToBeLoaded(); - cy.get(NUMBER_OF_ALERTS) + cy.get(ALERTS_COUNT) .invoke('text') .then((numberOfAlerts) => { const numberOfAlertsToBeClosed = 1; @@ -144,7 +141,7 @@ describe('Alerts', () => { waitForAlerts(); const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeClosed; - cy.get(NUMBER_OF_ALERTS).should('have.text', expectedNumberOfAlerts.toString()); + cy.get(ALERTS_COUNT).should('have.text', expectedNumberOfAlerts.toString()); cy.get(SHOWING_ALERTS).should( 'have.text', `Showing ${expectedNumberOfAlerts.toString()} alerts` @@ -153,7 +150,7 @@ describe('Alerts', () => { goToClosedAlerts(); waitForAlerts(); - cy.get(NUMBER_OF_ALERTS).should('have.text', numberOfAlertsToBeClosed.toString()); + cy.get(ALERTS_COUNT).should('have.text', numberOfAlertsToBeClosed.toString()); cy.get(SHOWING_ALERTS).should( 'have.text', `Showing ${numberOfAlertsToBeClosed.toString()} alert` @@ -178,7 +175,7 @@ describe('Alerts', () => { goToClosedAlerts(); waitForAlertsToBeLoaded(); - cy.get(NUMBER_OF_ALERTS) + cy.get(ALERTS_COUNT) .invoke('text') .then((numberOfAlerts) => { const numberOfAlertsToBeOpened = 1; @@ -195,7 +192,7 @@ describe('Alerts', () => { waitForAlerts(); const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeOpened; - cy.get(NUMBER_OF_ALERTS).should('have.text', expectedNumberOfAlerts.toString()); + cy.get(ALERTS_COUNT).should('have.text', expectedNumberOfAlerts.toString()); cy.get(SHOWING_ALERTS).should( 'have.text', `Showing ${expectedNumberOfAlerts.toString()} alerts` @@ -204,7 +201,7 @@ describe('Alerts', () => { goToOpenedAlerts(); waitForAlerts(); - cy.get(NUMBER_OF_ALERTS).should('have.text', numberOfAlertsToBeOpened.toString()); + cy.get(ALERTS_COUNT).should('have.text', numberOfAlertsToBeOpened.toString()); cy.get(SHOWING_ALERTS).should( 'have.text', `Showing ${numberOfAlertsToBeOpened.toString()} alert` @@ -228,7 +225,7 @@ describe('Alerts', () => { waitForAlerts(); waitForAlertsToBeLoaded(); - cy.get(NUMBER_OF_ALERTS) + cy.get(ALERTS_COUNT) .invoke('text') .then((numberOfAlerts) => { const numberOfAlertsToBeMarkedInProgress = 1; @@ -244,7 +241,7 @@ describe('Alerts', () => { waitForAlertsToBeLoaded(); const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeMarkedInProgress; - cy.get(NUMBER_OF_ALERTS).should('have.text', expectedNumberOfAlerts.toString()); + cy.get(ALERTS_COUNT).should('have.text', expectedNumberOfAlerts.toString()); cy.get(SHOWING_ALERTS).should( 'have.text', `Showing ${expectedNumberOfAlerts.toString()} alerts` @@ -253,10 +250,7 @@ describe('Alerts', () => { goToInProgressAlerts(); waitForAlerts(); - cy.get(NUMBER_OF_ALERTS).should( - 'have.text', - numberOfAlertsToBeMarkedInProgress.toString() - ); + cy.get(ALERTS_COUNT).should('have.text', numberOfAlertsToBeMarkedInProgress.toString()); cy.get(SHOWING_ALERTS).should( 'have.text', `Showing ${numberOfAlertsToBeMarkedInProgress.toString()} alert` diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_exceptions.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_exceptions.spec.ts index b1d7163ac70e0..160dbad9a06be 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_exceptions.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_exceptions.spec.ts @@ -6,8 +6,8 @@ import { exception } from '../objects/exception'; import { newRule } from '../objects/rule'; +import { ALERTS_COUNT, NUMBER_OF_ALERTS } from '../screens/alerts'; import { RULE_STATUS } from '../screens/create_new_rule'; -import { SERVER_SIDE_EVENT_COUNT } from '../screens/timeline'; import { addExceptionFromFirstAlert, @@ -52,7 +52,8 @@ describe('Exceptions', () => { waitForAlertsToPopulate(); refreshPage(); - cy.get(SERVER_SIDE_EVENT_COUNT) + cy.get(ALERTS_COUNT).should('exist'); + cy.get(NUMBER_OF_ALERTS) .invoke('text') .then((numberOfInitialAlertsText) => { cy.wrap(parseInt(numberOfInitialAlertsText, 10)).should( @@ -77,7 +78,8 @@ describe('Exceptions', () => { goToAlertsTab(); refreshPage(); - cy.get(SERVER_SIDE_EVENT_COUNT) + cy.get(ALERTS_COUNT).should('exist'); + cy.get(NUMBER_OF_ALERTS) .invoke('text') .then((numberOfAlertsAfterCreatingExceptionText) => { cy.wrap(parseInt(numberOfAlertsAfterCreatingExceptionText, 10)).should('eql', 0); @@ -86,7 +88,8 @@ describe('Exceptions', () => { goToClosedAlerts(); refreshPage(); - cy.get(SERVER_SIDE_EVENT_COUNT) + cy.get(ALERTS_COUNT).should('exist'); + cy.get(NUMBER_OF_ALERTS) .invoke('text') .then((numberOfClosedAlertsAfterCreatingExceptionText) => { cy.wrap(parseInt(numberOfClosedAlertsAfterCreatingExceptionText, 10)).should( @@ -99,7 +102,8 @@ describe('Exceptions', () => { waitForTheRuleToBeExecuted(); refreshPage(); - cy.get(SERVER_SIDE_EVENT_COUNT) + cy.get(ALERTS_COUNT).should('exist'); + cy.get(NUMBER_OF_ALERTS) .invoke('text') .then((numberOfOpenedAlertsAfterCreatingExceptionText) => { cy.wrap(parseInt(numberOfOpenedAlertsAfterCreatingExceptionText, 10)).should('eql', 0); @@ -113,7 +117,8 @@ describe('Exceptions', () => { waitForAlertsToPopulate(); refreshPage(); - cy.get(SERVER_SIDE_EVENT_COUNT) + cy.get(ALERTS_COUNT).should('exist'); + cy.get(NUMBER_OF_ALERTS) .invoke('text') .then((numberOfAlertsAfterRemovingExceptionsText) => { cy.wrap(parseInt(numberOfAlertsAfterRemovingExceptionsText, 10)).should( @@ -130,7 +135,8 @@ describe('Exceptions', () => { addsException(exception); esArchiverLoad('auditbeat_for_exceptions2'); - cy.get(SERVER_SIDE_EVENT_COUNT) + cy.get(ALERTS_COUNT).should('exist'); + cy.get(NUMBER_OF_ALERTS) .invoke('text') .then((numberOfAlertsAfterCreatingExceptionText) => { cy.wrap(parseInt(numberOfAlertsAfterCreatingExceptionText, 10)).should('eql', 0); @@ -139,7 +145,8 @@ describe('Exceptions', () => { goToClosedAlerts(); refreshPage(); - cy.get(SERVER_SIDE_EVENT_COUNT) + cy.get(ALERTS_COUNT).should('exist'); + cy.get(NUMBER_OF_ALERTS) .invoke('text') .then((numberOfClosedAlertsAfterCreatingExceptionText) => { cy.wrap(parseInt(numberOfClosedAlertsAfterCreatingExceptionText, 10)).should( @@ -152,7 +159,8 @@ describe('Exceptions', () => { waitForTheRuleToBeExecuted(); refreshPage(); - cy.get(SERVER_SIDE_EVENT_COUNT) + cy.get(ALERTS_COUNT).should('exist'); + cy.get(NUMBER_OF_ALERTS) .invoke('text') .then((numberOfOpenedAlertsAfterCreatingExceptionText) => { cy.wrap(parseInt(numberOfOpenedAlertsAfterCreatingExceptionText, 10)).should('eql', 0); @@ -165,7 +173,8 @@ describe('Exceptions', () => { waitForAlertsToPopulate(); refreshPage(); - cy.get(SERVER_SIDE_EVENT_COUNT) + cy.get(ALERTS_COUNT).should('exist'); + cy.get(NUMBER_OF_ALERTS) .invoke('text') .then((numberOfAlertsAfterRemovingExceptionsText) => { cy.wrap(parseInt(numberOfAlertsAfterRemovingExceptionsText, 10)).should( diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_indicator_match.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_indicator_match.spec.ts new file mode 100644 index 0000000000000..03e714f2381c6 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_indicator_match.spec.ts @@ -0,0 +1,197 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { newThreatIndicatorRule } from '../objects/rule'; + +import { + ALERT_RULE_METHOD, + ALERT_RULE_NAME, + ALERT_RULE_RISK_SCORE, + ALERT_RULE_SEVERITY, + ALERT_RULE_VERSION, + NUMBER_OF_ALERTS, +} from '../screens/alerts'; +import { + CUSTOM_RULES_BTN, + RISK_SCORE, + RULE_NAME, + RULES_ROW, + RULES_TABLE, + RULE_SWITCH, + SEVERITY, +} from '../screens/alerts_detection_rules'; +import { + ABOUT_DETAILS, + ABOUT_INVESTIGATION_NOTES, + ABOUT_RULE_DESCRIPTION, + ADDITIONAL_LOOK_BACK_DETAILS, + CUSTOM_QUERY_DETAILS, + DEFINITION_DETAILS, + FALSE_POSITIVES_DETAILS, + getDetails, + INDEX_PATTERNS_DETAILS, + INDICATOR_INDEX_PATTERNS, + INDICATOR_INDEX_QUERY, + INDICATOR_MAPPING, + INVESTIGATION_NOTES_MARKDOWN, + INVESTIGATION_NOTES_TOGGLE, + MITRE_ATTACK_DETAILS, + REFERENCE_URLS_DETAILS, + removeExternalLinkText, + RISK_SCORE_DETAILS, + RULE_NAME_HEADER, + RULE_TYPE_DETAILS, + RUNS_EVERY_DETAILS, + SCHEDULE_DETAILS, + SEVERITY_DETAILS, + TAGS_DETAILS, + TIMELINE_TEMPLATE_DETAILS, +} from '../screens/rule_details'; + +import { + goToManageAlertsDetectionRules, + waitForAlertsIndexToBeCreated, + waitForAlertsPanelToBeLoaded, +} from '../tasks/alerts'; +import { + changeToThreeHundredRowsPerPage, + deleteRule, + filterByCustomRules, + goToCreateNewRule, + goToRuleDetails, + waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, + waitForRulesToBeLoaded, +} from '../tasks/alerts_detection_rules'; +import { removeSignalsIndex } from '../tasks/api_calls'; +import { + createAndActivateRule, + fillAboutRuleAndContinue, + fillDefineIndicatorMatchRuleAndContinue, + fillScheduleRuleAndContinue, + selectIndicatorMatchType, + waitForAlertsToPopulate, + waitForTheRuleToBeExecuted, +} from '../tasks/create_new_rule'; +import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; +import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; + +import { DETECTIONS_URL } from '../urls/navigation'; + +const expectedUrls = newThreatIndicatorRule.referenceUrls.join(''); +const expectedFalsePositives = newThreatIndicatorRule.falsePositivesExamples.join(''); +const expectedTags = newThreatIndicatorRule.tags.join(''); +const expectedMitre = newThreatIndicatorRule.mitre + .map(function (mitre) { + return mitre.tactic + mitre.techniques.join(''); + }) + .join(''); +const expectedNumberOfRules = 1; +const expectedNumberOfAlerts = 1; + +describe('Detection rules, Indicator Match', () => { + beforeEach(() => { + esArchiverLoad('threat_indicator'); + esArchiverLoad('threat_data'); + }); + + afterEach(() => { + esArchiverUnload('threat_indicator'); + esArchiverUnload('threat_data'); + removeSignalsIndex(); + deleteRule(); + }); + + it('Creates and activates a new Indicator Match rule', () => { + loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); + waitForAlertsPanelToBeLoaded(); + waitForAlertsIndexToBeCreated(); + goToManageAlertsDetectionRules(); + waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded(); + goToCreateNewRule(); + selectIndicatorMatchType(); + fillDefineIndicatorMatchRuleAndContinue(newThreatIndicatorRule); + fillAboutRuleAndContinue(newThreatIndicatorRule); + fillScheduleRuleAndContinue(newThreatIndicatorRule); + createAndActivateRule(); + + cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); + + changeToThreeHundredRowsPerPage(); + waitForRulesToBeLoaded(); + + cy.get(RULES_TABLE).then(($table) => { + cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRules); + }); + + filterByCustomRules(); + + cy.get(RULES_TABLE).then(($table) => { + cy.wrap($table.find(RULES_ROW).length).should('eql', 1); + }); + cy.get(RULE_NAME).should('have.text', newThreatIndicatorRule.name); + cy.get(RISK_SCORE).should('have.text', newThreatIndicatorRule.riskScore); + cy.get(SEVERITY).should('have.text', newThreatIndicatorRule.severity); + cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); + + goToRuleDetails(); + + cy.get(RULE_NAME_HEADER).should('have.text', `${newThreatIndicatorRule.name}`); + cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', newThreatIndicatorRule.description); + cy.get(ABOUT_DETAILS).within(() => { + getDetails(SEVERITY_DETAILS).should('have.text', newThreatIndicatorRule.severity); + getDetails(RISK_SCORE_DETAILS).should('have.text', newThreatIndicatorRule.riskScore); + getDetails(REFERENCE_URLS_DETAILS).should((details) => { + expect(removeExternalLinkText(details.text())).equal(expectedUrls); + }); + getDetails(FALSE_POSITIVES_DETAILS).should('have.text', expectedFalsePositives); + getDetails(MITRE_ATTACK_DETAILS).should((mitre) => { + expect(removeExternalLinkText(mitre.text())).equal(expectedMitre); + }); + getDetails(TAGS_DETAILS).should('have.text', expectedTags); + }); + cy.get(INVESTIGATION_NOTES_TOGGLE).click({ force: true }); + cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); + + cy.get(DEFINITION_DETAILS).within(() => { + getDetails(INDEX_PATTERNS_DETAILS).should('have.text', newThreatIndicatorRule.index.join('')); + getDetails(CUSTOM_QUERY_DETAILS).should('have.text', '*:*'); + getDetails(RULE_TYPE_DETAILS).should('have.text', 'Indicator Match'); + getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); + getDetails(INDICATOR_INDEX_PATTERNS).should( + 'have.text', + newThreatIndicatorRule.indicatorIndexPattern.join('') + ); + getDetails(INDICATOR_MAPPING).should( + 'have.text', + `${newThreatIndicatorRule.indicatorMapping} MATCHES ${newThreatIndicatorRule.indicatorIndexField}` + ); + getDetails(INDICATOR_INDEX_QUERY).should('have.text', '*:*'); + }); + + cy.get(SCHEDULE_DETAILS).within(() => { + getDetails(RUNS_EVERY_DETAILS).should( + 'have.text', + `${newThreatIndicatorRule.runsEvery.interval}${newThreatIndicatorRule.runsEvery.type}` + ); + getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should( + 'have.text', + `${newThreatIndicatorRule.lookBack.interval}${newThreatIndicatorRule.lookBack.type}` + ); + }); + + waitForTheRuleToBeExecuted(); + waitForAlertsToPopulate(); + + cy.get(NUMBER_OF_ALERTS).should('have.text', expectedNumberOfAlerts); + cy.get(ALERT_RULE_NAME).first().should('have.text', newThreatIndicatorRule.name); + cy.get(ALERT_RULE_VERSION).first().should('have.text', '1'); + cy.get(ALERT_RULE_METHOD).first().should('have.text', 'threat_match'); + cy.get(ALERT_RULE_SEVERITY) + .first() + .should('have.text', newThreatIndicatorRule.severity.toLowerCase()); + cy.get(ALERT_RULE_RISK_SCORE).first().should('have.text', newThreatIndicatorRule.riskScore); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/objects/rule.ts b/x-pack/plugins/security_solution/cypress/objects/rule.ts index 8ba545e242b47..06046b9385712 100644 --- a/x-pack/plugins/security_solution/cypress/objects/rule.ts +++ b/x-pack/plugins/security_solution/cypress/objects/rule.ts @@ -30,10 +30,10 @@ interface Interval { } export interface CustomRule { - customQuery: string; + customQuery?: string; name: string; description: string; - index?: string[]; + index: string[]; interval?: string; severity: string; riskScore: string; @@ -43,7 +43,7 @@ export interface CustomRule { falsePositivesExamples: string[]; mitre: Mitre[]; note: string; - timelineId: string; + timelineId?: string; runsEvery: Interval; lookBack: Interval; } @@ -60,6 +60,12 @@ export interface OverrideRule extends CustomRule { timestampOverride: string; } +export interface ThreatIndicatorRule extends CustomRule { + indicatorIndexPattern: string[]; + indicatorMapping: string; + indicatorIndexField: string; +} + export interface MachineLearningRule { machineLearningJob: string; anomalyScoreThreshold: string; @@ -77,6 +83,16 @@ export interface MachineLearningRule { lookBack: Interval; } +export const indexPatterns = [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', +]; + const mitre1: Mitre = { tactic: 'Discovery (TA0007)', techniques: ['Cloud Service Discovery (T1526)', 'File and Directory Discovery (T1083)'], @@ -121,6 +137,7 @@ const lookBack: Interval = { export const newRule: CustomRule = { customQuery: 'host.name:*', + index: indexPatterns, name: 'New Rule Test', description: 'The new rule description.', severity: 'High', @@ -162,6 +179,7 @@ export const existingRule: CustomRule = { export const newOverrideRule: OverrideRule = { customQuery: 'host.name:*', + index: indexPatterns, name: 'New Rule Test', description: 'The new rule description.', severity: 'High', @@ -182,6 +200,7 @@ export const newOverrideRule: OverrideRule = { export const newThresholdRule: ThresholdRule = { customQuery: 'host.name:*', + index: indexPatterns, name: 'New Rule Test', description: 'The new rule description.', severity: 'High', @@ -217,6 +236,7 @@ export const machineLearningRule: MachineLearningRule = { export const eqlRule: CustomRule = { customQuery: 'any where process.name == "which"', name: 'New EQL Rule', + index: indexPatterns, description: 'New EQL rule description.', severity: 'High', riskScore: '17', @@ -236,6 +256,7 @@ export const eqlSequenceRule: CustomRule = { [any where process.name == "which"]\ [any where process.name == "xargs"]', name: 'New EQL Sequence Rule', + index: indexPatterns, description: 'New EQL rule description.', severity: 'High', riskScore: '17', @@ -249,15 +270,23 @@ export const eqlSequenceRule: CustomRule = { lookBack, }; -export const indexPatterns = [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'packetbeat-*', - 'winlogbeat-*', -]; +export const newThreatIndicatorRule: ThreatIndicatorRule = { + name: 'Threat Indicator Rule Test', + description: 'The threat indicator rule description.', + index: ['threat-data-*'], + severity: 'Critical', + riskScore: '20', + tags: ['test', 'threat'], + referenceUrls: ['https://www.google.com/', 'https://elastic.co/'], + falsePositivesExamples: ['False1', 'False2'], + mitre: [mitre1, mitre2], + note: '# test markdown', + runsEvery, + lookBack, + indicatorIndexPattern: ['threat-indicator-*'], + indicatorMapping: 'agent.id', + indicatorIndexField: 'agent.threat', +}; export const severitiesOverride = ['Low', 'Medium', 'High', 'Critical']; diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts.ts b/x-pack/plugins/security_solution/cypress/screens/alerts.ts index 2c80d02cad83d..bc3be900284b4 100644 --- a/x-pack/plugins/security_solution/cypress/screens/alerts.ts +++ b/x-pack/plugins/security_solution/cypress/screens/alerts.ts @@ -8,6 +8,8 @@ export const ADD_EXCEPTION_BTN = '[data-test-subj="addExceptionButton"]'; export const ALERTS = '[data-test-subj="event"]'; +export const ALERTS_COUNT = '[data-test-subj="server-side-event-count"]'; + export const ALERT_CHECKBOX = '[data-test-subj="select-event-container"] .euiCheckbox__input'; export const ALERT_ID = '[data-test-subj="draggable-content-_id"]'; @@ -43,7 +45,7 @@ export const MARK_ALERT_IN_PROGRESS_BTN = '[data-test-subj="in-progress-alert-st export const MARK_SELECTED_ALERTS_IN_PROGRESS_BTN = '[data-test-subj="markSelectedAlertsInProgressButton"]'; -export const NUMBER_OF_ALERTS = '[data-test-subj="server-side-event-count"] .euiBadge__text'; +export const NUMBER_OF_ALERTS = '[data-test-subj="local-events-count"]'; export const OPEN_ALERT_BTN = '[data-test-subj="open-alert-status"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts index d802e97363a68..ab9347f1862cc 100644 --- a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts +++ b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts @@ -27,8 +27,12 @@ export const MITRE_BTN = '[data-test-subj="addMitre"]'; export const ADVANCED_SETTINGS_BTN = '[data-test-subj="advancedSettings"] .euiAccordion__button'; +export const COMBO_BOX_CLEAR_BTN = '[data-test-subj="comboBoxClearButton"]'; + export const COMBO_BOX_INPUT = '[data-test-subj="comboBoxInput"]'; +export const COMBO_BOX_RESULT = '.euiFilterSelectItem'; + export const CREATE_AND_ACTIVATE_BTN = '[data-test-subj="create-activate"]'; export const CUSTOM_QUERY_INPUT = @@ -57,6 +61,8 @@ export const EQL_QUERY_VALIDATION_SPINNER = '[data-test-subj="eql-validation-loa export const IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK = '[data-test-subj="importQueryFromSavedTimeline"]'; +export const INDICATOR_MATCH_TYPE = '[data-test-subj="threatMatchRuleType"]'; + export const INPUT = '[data-test-subj="input"]'; export const INVESTIGATION_NOTES_TEXTAREA = diff --git a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts index 8e93d5dcd6315..ad969b54ffd90 100644 --- a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts @@ -36,6 +36,12 @@ export const FALSE_POSITIVES_DETAILS = 'False positive examples'; export const INDEX_PATTERNS_DETAILS = 'Index patterns'; +export const INDICATOR_INDEX_PATTERNS = 'Indicator index patterns'; + +export const INDICATOR_INDEX_QUERY = 'Indicator index query'; + +export const INDICATOR_MAPPING = 'Indicator mapping'; + export const INVESTIGATION_NOTES_MARKDOWN = 'test markdown'; export const INVESTIGATION_NOTES_TOGGLE = '[data-test-subj="stepAboutDetailsToggle-notes"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts index 9b809dbe524ae..219c6496ee893 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts @@ -9,6 +9,7 @@ import { MachineLearningRule, machineLearningRule, OverrideRule, + ThreatIndicatorRule, ThresholdRule, } from '../objects/rule'; import { @@ -26,6 +27,7 @@ import { DEFINE_EDIT_TAB, FALSE_POSITIVES_INPUT, IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK, + INDICATOR_MATCH_TYPE, INPUT, INVESTIGATION_NOTES_TEXTAREA, LOOK_BACK_INTERVAL, @@ -63,11 +65,13 @@ import { QUERY_PREVIEW_BUTTON, EQL_QUERY_PREVIEW_HISTOGRAM, EQL_QUERY_VALIDATION_SPINNER, + COMBO_BOX_CLEAR_BTN, + COMBO_BOX_RESULT, } from '../screens/create_new_rule'; -import { SERVER_SIDE_EVENT_COUNT } from '../screens/timeline'; import { NOTIFICATION_TOASTS, TOAST_ERROR_CLASS } from '../screens/shared'; import { TIMELINE } from '../screens/timelines'; import { refreshPage } from './security_header'; +import { NUMBER_OF_ALERTS } from '../screens/alerts'; export const createAndActivateRule = () => { cy.get(SCHEDULE_CONTINUE_BUTTON).click({ force: true }); @@ -75,7 +79,9 @@ export const createAndActivateRule = () => { cy.get(CREATE_AND_ACTIVATE_BTN).should('not.exist'); }; -export const fillAboutRule = (rule: CustomRule | MachineLearningRule | ThresholdRule) => { +export const fillAboutRule = ( + rule: CustomRule | MachineLearningRule | ThresholdRule | ThreatIndicatorRule +) => { cy.get(RULE_NAME_INPUT).clear({ force: true }).type(rule.name, { force: true }); cy.get(RULE_DESCRIPTION_INPUT).clear({ force: true }).type(rule.description, { force: true }); @@ -121,7 +127,7 @@ export const fillAboutRule = (rule: CustomRule | MachineLearningRule | Threshold }; export const fillAboutRuleAndContinue = ( - rule: CustomRule | MachineLearningRule | ThresholdRule + rule: CustomRule | MachineLearningRule | ThresholdRule | ThreatIndicatorRule ) => { fillAboutRule(rule); cy.get(ABOUT_CONTINUE_BTN).should('exist').click({ force: true }); @@ -195,7 +201,7 @@ export const fillDefineCustomRuleWithImportedQueryAndContinue = ( rule: CustomRule | OverrideRule ) => { cy.get(IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK).click(); - cy.get(TIMELINE(rule.timelineId)).click(); + cy.get(TIMELINE(rule.timelineId!)).click(); cy.get(CUSTOM_QUERY_INPUT).should('have.value', rule.customQuery); cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true }); @@ -213,7 +219,7 @@ export const fillDefineThresholdRuleAndContinue = (rule: ThresholdRule) => { const thresholdField = 0; const threshold = 1; - cy.get(CUSTOM_QUERY_INPUT).type(rule.customQuery); + cy.get(CUSTOM_QUERY_INPUT).type(rule.customQuery!); cy.get(CUSTOM_QUERY_INPUT).should('have.value', rule.customQuery); cy.get(THRESHOLD_INPUT_AREA) .find(INPUT) @@ -228,7 +234,7 @@ export const fillDefineThresholdRuleAndContinue = (rule: ThresholdRule) => { }; export const fillDefineEqlRuleAndContinue = (rule: CustomRule) => { - cy.get(EQL_QUERY_INPUT).type(rule.customQuery); + cy.get(EQL_QUERY_INPUT).type(rule.customQuery!); cy.get(EQL_QUERY_VALIDATION_SPINNER).should('not.exist'); cy.get(QUERY_PREVIEW_BUTTON).should('not.be.disabled').click({ force: true }); cy.get(EQL_QUERY_PREVIEW_HISTOGRAM).should('contain.text', 'Hits'); @@ -238,6 +244,22 @@ export const fillDefineEqlRuleAndContinue = (rule: CustomRule) => { cy.get(EQL_QUERY_INPUT).should('not.exist'); }; +export const fillDefineIndicatorMatchRuleAndContinue = (rule: ThreatIndicatorRule) => { + const INDEX_PATTERNS = 0; + const INDICATOR_INDEX_PATTERN = 2; + const INDICATOR_MAPPING = 3; + const INDICATOR_INDEX_FIELD = 4; + + cy.get(COMBO_BOX_CLEAR_BTN).click(); + cy.get(COMBO_BOX_INPUT).eq(INDEX_PATTERNS).type(`${rule.index}{enter}`); + cy.get(COMBO_BOX_INPUT).eq(INDICATOR_INDEX_PATTERN).type(`${rule.indicatorIndexPattern}{enter}`); + cy.get(COMBO_BOX_INPUT).eq(INDICATOR_MAPPING).type(`${rule.indicatorMapping}{enter}`); + cy.get(COMBO_BOX_RESULT).first().click(); + cy.get(COMBO_BOX_INPUT).eq(INDICATOR_INDEX_FIELD).type(`${rule.indicatorIndexField}{enter}`); + cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true }); + cy.get(CUSTOM_QUERY_INPUT).should('not.exist'); +}; + export const fillDefineMachineLearningRuleAndContinue = (rule: MachineLearningRule) => { cy.get(MACHINE_LEARNING_DROPDOWN).click({ force: true }); cy.contains(MACHINE_LEARNING_LIST, rule.machineLearningJob).click(); @@ -265,6 +287,14 @@ export const goToActionsStepTab = () => { cy.get(ACTIONS_EDIT_TAB).click({ force: true }); }; +export const selectEqlRuleType = () => { + cy.get(EQL_TYPE).click({ force: true }); +}; + +export const selectIndicatorMatchType = () => { + cy.get(INDICATOR_MATCH_TYPE).click({ force: true }); +}; + export const selectMachineLearningRuleType = () => { cy.get(MACHINE_LEARNING_TYPE).click({ force: true }); }; @@ -273,22 +303,6 @@ export const selectThresholdRuleType = () => { cy.get(THRESHOLD_TYPE).click({ force: true }); }; -export const waitForAlertsToPopulate = async () => { - cy.waitUntil( - () => { - refreshPage(); - return cy - .get(SERVER_SIDE_EVENT_COUNT) - .invoke('text') - .then((countText) => { - const alertCount = parseInt(countText, 10) || 0; - return alertCount > 0; - }); - }, - { interval: 500, timeout: 12000 } - ); -}; - export const waitForTheRuleToBeExecuted = () => { cy.waitUntil(() => { cy.get(REFRESH_BUTTON).click(); @@ -299,6 +313,15 @@ export const waitForTheRuleToBeExecuted = () => { }); }; -export const selectEqlRuleType = () => { - cy.get(EQL_TYPE).click({ force: true }); +export const waitForAlertsToPopulate = async () => { + cy.waitUntil(() => { + refreshPage(); + return cy + .get(NUMBER_OF_ALERTS) + .invoke('text') + .then((countText) => { + const alertCount = parseInt(countText, 10) || 0; + return alertCount > 0; + }); + }); }; diff --git a/x-pack/test/security_solution_cypress/es_archives/threat_data/data.json.gz b/x-pack/test/security_solution_cypress/es_archives/threat_data/data.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..ab63f9a47a7baa9e6ea0e830f55ec8c5e48df4c9 GIT binary patch literal 1086 zcmV-E1i||siwFpR@w;CD17u-zVJ>QOZ*Bm+R!eW&I1s+)R|GzFfyyCeJq;|d2+~8b zD0*lX1+rKe6g9HBmPmo5?6^h#`;xNkkdlISFAah?o;PQ{`N;1#x3>#@YGJXyU6g_@ z-dn+e)SZ=lH($(GR$A=_o<5|_@&0rBl|3Bci@x8S&8-D5;n^DLodlwTl4uejgfDs} zI!Rw68p$7;HJ~(UTI&`foCnDK;zxwm5niKYiE#Qf_#1n&1+JX{Mg;8+8jz&koC{2F zM#DUTAdagvh;o90EJJC4(yTNJicpze0~-IGP@0pbKf3B9qqb-!j>I)Ohej((3q&EP zl9&cjnC1aVY{_wj%eW{ILWS#f=_u(+rVG;%S9t)bnBZ2QEzuG!2Gz^;u(W2A(-tQU z%7`N5R@ZkAqa_Y)s1Un(T0-}rtq*pkLfXiyEQ+$3#G)(xyyQSwO$t^secF5zygyf` z0%|HWy~lyyE^cPZy-_=D%u^mqu6maX7l5?TD&-!8bWuBPZC`^&v9TY zDTyqDa6UpS#lJxHe5p_qr5OzrgXT^511mvV>n&}kQ!EX>87KNY>zPr8GowuMWf(`x z;q#}*nW0H~pvq6{;0`bG9PZ#SfgPbk{RY7(9<*>xA(yr0(iaMfBKUb=@<4jd`u7cll-u| zzf+$-Ztp(+?(I2~a3vJc=|Xg7WoJn)bgxrMxEh#lp}lrp37@rxXu2GRq$wyh-jA&s z1Lm$%@~&X~u083U;48nYSM64aZ4Dc9PtyHH?cum72{h(Bv+$z!F$4~OWkHxf;>1wP zI$jxqM;?E{Gtf?x;!IWJik9gdCyWa6df87W4dY2y6v#t=asBd3$!2Dw=fQP^136Ef z#;?a;^&A=s^1)-DbYoH&ZZuzNC%WxtfZmV9-K==tm~m}b!@P36`x`BNh+fHMV{6%v zvXp1sFVJ&kezGb`qGXjog3#D;sKyb#+>HNwZAz!c(D~Ub>*n(J<>uw)KU(UR5_${( E0C|!QdjJ3c literal 0 HcmV?d00001 diff --git a/x-pack/test/security_solution_cypress/es_archives/threat_data/mappings.json b/x-pack/test/security_solution_cypress/es_archives/threat_data/mappings.json new file mode 100644 index 0000000000000..3ccdee6bdb5eb --- /dev/null +++ b/x-pack/test/security_solution_cypress/es_archives/threat_data/mappings.json @@ -0,0 +1,3577 @@ +{ + "type": "index", + "value": { + "aliases": { + "thread-data": { + "is_write_index": false + }, + "beats": { + }, + "siem-read-alias": { + } + }, + "index": "threat-data-001", + "mappings": { + "_meta": { + "beat": "auditbeat", + "version": "8.0.0" + }, + "date_detection": false, + "dynamic_templates": [ + { + "labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "labels.*" + } + }, + { + "container.labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "container.labels.*" + } + }, + { + "fields": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "fields.*" + } + }, + { + "docker.container.labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "docker.container.labels.*" + } + }, + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "auditd": { + "properties": { + "data": { + "properties": { + "a0": { + "ignore_above": 1024, + "type": "keyword" + }, + "a1": { + "ignore_above": 1024, + "type": "keyword" + }, + "a2": { + "ignore_above": 1024, + "type": "keyword" + }, + "a3": { + "ignore_above": 1024, + "type": "keyword" + }, + "a[0-3]": { + "ignore_above": 1024, + "type": "keyword" + }, + "acct": { + "ignore_above": 1024, + "type": "keyword" + }, + "acl": { + "ignore_above": 1024, + "type": "keyword" + }, + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "added": { + "ignore_above": 1024, + "type": "keyword" + }, + "addr": { + "ignore_above": 1024, + "type": "keyword" + }, + "apparmor": { + "ignore_above": 1024, + "type": "keyword" + }, + "arch": { + "ignore_above": 1024, + "type": "keyword" + }, + "argc": { + "ignore_above": 1024, + "type": "keyword" + }, + "audit_backlog_limit": { + "ignore_above": 1024, + "type": "keyword" + }, + "audit_backlog_wait_time": { + "ignore_above": 1024, + "type": "keyword" + }, + "audit_enabled": { + "ignore_above": 1024, + "type": "keyword" + }, + "audit_failure": { + "ignore_above": 1024, + "type": "keyword" + }, + "banners": { + "ignore_above": 1024, + "type": "keyword" + }, + "bool": { + "ignore_above": 1024, + "type": "keyword" + }, + "bus": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_fe": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_fi": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_fp": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_fver": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_pe": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_pi": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_pp": { + "ignore_above": 1024, + "type": "keyword" + }, + "capability": { + "ignore_above": 1024, + "type": "keyword" + }, + "cgroup": { + "ignore_above": 1024, + "type": "keyword" + }, + "changed": { + "ignore_above": 1024, + "type": "keyword" + }, + "cipher": { + "ignore_above": 1024, + "type": "keyword" + }, + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "cmd": { + "ignore_above": 1024, + "type": "keyword" + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "compat": { + "ignore_above": 1024, + "type": "keyword" + }, + "daddr": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "default-context": { + "ignore_above": 1024, + "type": "keyword" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "dir": { + "ignore_above": 1024, + "type": "keyword" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "dmac": { + "ignore_above": 1024, + "type": "keyword" + }, + "dport": { + "ignore_above": 1024, + "type": "keyword" + }, + "enforcing": { + "ignore_above": 1024, + "type": "keyword" + }, + "entries": { + "ignore_above": 1024, + "type": "keyword" + }, + "exit": { + "ignore_above": 1024, + "type": "keyword" + }, + "fam": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "fd": { + "ignore_above": 1024, + "type": "keyword" + }, + "fe": { + "ignore_above": 1024, + "type": "keyword" + }, + "feature": { + "ignore_above": 1024, + "type": "keyword" + }, + "fi": { + "ignore_above": 1024, + "type": "keyword" + }, + "file": { + "ignore_above": 1024, + "type": "keyword" + }, + "flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "format": { + "ignore_above": 1024, + "type": "keyword" + }, + "fp": { + "ignore_above": 1024, + "type": "keyword" + }, + "fver": { + "ignore_above": 1024, + "type": "keyword" + }, + "grantors": { + "ignore_above": 1024, + "type": "keyword" + }, + "grp": { + "ignore_above": 1024, + "type": "keyword" + }, + "hook": { + "ignore_above": 1024, + "type": "keyword" + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "icmp_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "igid": { + "ignore_above": 1024, + "type": "keyword" + }, + "img-ctx": { + "ignore_above": 1024, + "type": "keyword" + }, + "info": { + "ignore_above": 1024, + "type": "keyword" + }, + "inif": { + "ignore_above": 1024, + "type": "keyword" + }, + "ino": { + "ignore_above": 1024, + "type": "keyword" + }, + "inode_gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "inode_uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "invalid_context": { + "ignore_above": 1024, + "type": "keyword" + }, + "ioctlcmd": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "ignore_above": 1024, + "type": "keyword" + }, + "ipid": { + "ignore_above": 1024, + "type": "keyword" + }, + "ipx-net": { + "ignore_above": 1024, + "type": "keyword" + }, + "items": { + "ignore_above": 1024, + "type": "keyword" + }, + "iuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "ksize": { + "ignore_above": 1024, + "type": "keyword" + }, + "laddr": { + "ignore_above": 1024, + "type": "keyword" + }, + "len": { + "ignore_above": 1024, + "type": "keyword" + }, + "list": { + "ignore_above": 1024, + "type": "keyword" + }, + "lport": { + "ignore_above": 1024, + "type": "keyword" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "macproto": { + "ignore_above": 1024, + "type": "keyword" + }, + "maj": { + "ignore_above": 1024, + "type": "keyword" + }, + "major": { + "ignore_above": 1024, + "type": "keyword" + }, + "minor": { + "ignore_above": 1024, + "type": "keyword" + }, + "model": { + "ignore_above": 1024, + "type": "keyword" + }, + "msg": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "nargs": { + "ignore_above": 1024, + "type": "keyword" + }, + "net": { + "ignore_above": 1024, + "type": "keyword" + }, + "new": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-chardev": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-disk": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-enabled": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-fs": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-level": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-log_passwd": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-mem": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-net": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-range": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-rng": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-role": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-seuser": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-vcpu": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_lock": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_pe": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_pi": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_pp": { + "ignore_above": 1024, + "type": "keyword" + }, + "nlnk-fam": { + "ignore_above": 1024, + "type": "keyword" + }, + "nlnk-grp": { + "ignore_above": 1024, + "type": "keyword" + }, + "nlnk-pid": { + "ignore_above": 1024, + "type": "keyword" + }, + "oauid": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "ocomm": { + "ignore_above": 1024, + "type": "keyword" + }, + "oflag": { + "ignore_above": 1024, + "type": "keyword" + }, + "old": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-auid": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-chardev": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-disk": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-enabled": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-fs": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-level": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-log_passwd": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-mem": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-net": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-range": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-rng": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-role": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-ses": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-seuser": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-vcpu": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_enforcing": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_lock": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_pa": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_pe": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_pi": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_pp": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_prom": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_val": { + "ignore_above": 1024, + "type": "keyword" + }, + "op": { + "ignore_above": 1024, + "type": "keyword" + }, + "operation": { + "ignore_above": 1024, + "type": "keyword" + }, + "opid": { + "ignore_above": 1024, + "type": "keyword" + }, + "oses": { + "ignore_above": 1024, + "type": "keyword" + }, + "outif": { + "ignore_above": 1024, + "type": "keyword" + }, + "pa": { + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { + "ignore_above": 1024, + "type": "keyword" + }, + "per": { + "ignore_above": 1024, + "type": "keyword" + }, + "perm": { + "ignore_above": 1024, + "type": "keyword" + }, + "perm_mask": { + "ignore_above": 1024, + "type": "keyword" + }, + "permissive": { + "ignore_above": 1024, + "type": "keyword" + }, + "pfs": { + "ignore_above": 1024, + "type": "keyword" + }, + "pi": { + "ignore_above": 1024, + "type": "keyword" + }, + "pp": { + "ignore_above": 1024, + "type": "keyword" + }, + "printer": { + "ignore_above": 1024, + "type": "keyword" + }, + "profile": { + "ignore_above": 1024, + "type": "keyword" + }, + "prom": { + "ignore_above": 1024, + "type": "keyword" + }, + "proto": { + "ignore_above": 1024, + "type": "keyword" + }, + "qbytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "range": { + "ignore_above": 1024, + "type": "keyword" + }, + "reason": { + "ignore_above": 1024, + "type": "keyword" + }, + "removed": { + "ignore_above": 1024, + "type": "keyword" + }, + "res": { + "ignore_above": 1024, + "type": "keyword" + }, + "resrc": { + "ignore_above": 1024, + "type": "keyword" + }, + "rport": { + "ignore_above": 1024, + "type": "keyword" + }, + "sauid": { + "ignore_above": 1024, + "type": "keyword" + }, + "scontext": { + "ignore_above": 1024, + "type": "keyword" + }, + "selected-context": { + "ignore_above": 1024, + "type": "keyword" + }, + "seperm": { + "ignore_above": 1024, + "type": "keyword" + }, + "seperms": { + "ignore_above": 1024, + "type": "keyword" + }, + "seqno": { + "ignore_above": 1024, + "type": "keyword" + }, + "seresult": { + "ignore_above": 1024, + "type": "keyword" + }, + "ses": { + "ignore_above": 1024, + "type": "keyword" + }, + "seuser": { + "ignore_above": 1024, + "type": "keyword" + }, + "sig": { + "ignore_above": 1024, + "type": "keyword" + }, + "sigev_signo": { + "ignore_above": 1024, + "type": "keyword" + }, + "smac": { + "ignore_above": 1024, + "type": "keyword" + }, + "socket": { + "properties": { + "addr": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "ignore_above": 1024, + "type": "keyword" + }, + "saddr": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "spid": { + "ignore_above": 1024, + "type": "keyword" + }, + "sport": { + "ignore_above": 1024, + "type": "keyword" + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "subj": { + "ignore_above": 1024, + "type": "keyword" + }, + "success": { + "ignore_above": 1024, + "type": "keyword" + }, + "syscall": { + "ignore_above": 1024, + "type": "keyword" + }, + "table": { + "ignore_above": 1024, + "type": "keyword" + }, + "tclass": { + "ignore_above": 1024, + "type": "keyword" + }, + "tcontext": { + "ignore_above": 1024, + "type": "keyword" + }, + "terminal": { + "ignore_above": 1024, + "type": "keyword" + }, + "tty": { + "ignore_above": 1024, + "type": "keyword" + }, + "unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "uri": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "val": { + "ignore_above": 1024, + "type": "keyword" + }, + "ver": { + "ignore_above": 1024, + "type": "keyword" + }, + "virt": { + "ignore_above": 1024, + "type": "keyword" + }, + "vm": { + "ignore_above": 1024, + "type": "keyword" + }, + "vm-ctx": { + "ignore_above": 1024, + "type": "keyword" + }, + "vm-pid": { + "ignore_above": 1024, + "type": "keyword" + }, + "watch": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "message_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "paths": { + "properties": { + "cap_fe": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_fi": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_fp": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_fver": { + "ignore_above": 1024, + "type": "keyword" + }, + "dev": { + "ignore_above": 1024, + "type": "keyword" + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "item": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "nametype": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_level": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_role": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_user": { + "ignore_above": 1024, + "type": "keyword" + }, + "objtype": { + "ignore_above": 1024, + "type": "keyword" + }, + "ogid": { + "ignore_above": 1024, + "type": "keyword" + }, + "ouid": { + "ignore_above": 1024, + "type": "keyword" + }, + "rdev": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "result": { + "ignore_above": 1024, + "type": "keyword" + }, + "sequence": { + "type": "long" + }, + "session": { + "ignore_above": 1024, + "type": "keyword" + }, + "summary": { + "properties": { + "actor": { + "properties": { + "primary": { + "ignore_above": 1024, + "type": "keyword" + }, + "secondary": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "how": { + "ignore_above": 1024, + "type": "keyword" + }, + "object": { + "properties": { + "primary": { + "ignore_above": 1024, + "type": "keyword" + }, + "secondary": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "client": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "user": { + "properties": { + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "cloud": { + "properties": { + "account": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "availability_zone": { + "ignore_above": 1024, + "type": "keyword" + }, + "instance": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "machine": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "project": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "region": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "container": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "image": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "tag": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "type": "object" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "runtime": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "packets": { + "type": "long" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "user": { + "properties": { + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "docker": { + "properties": { + "container": { + "properties": { + "labels": { + "type": "object" + } + } + } + } + }, + "ecs": { + "properties": { + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "error": { + "properties": { + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "message": { + "norms": false, + "type": "text" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "event": { + "properties": { + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "dataset": { + "ignore_above": 1024, + "type": "keyword" + }, + "duration": { + "type": "long" + }, + "end": { + "type": "date" + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "origin": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "ignore_above": 1024, + "type": "keyword" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "risk_score_norm": { + "type": "float" + }, + "severity": { + "type": "long" + }, + "start": { + "type": "date" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "fields": { + "type": "object" + }, + "file": { + "properties": { + "ctime": { + "type": "date" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mtime": { + "type": "date" + }, + "origin": { + "fields": { + "raw": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "selinux": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "role": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "setgid": { + "type": "boolean" + }, + "setuid": { + "type": "boolean" + }, + "size": { + "type": "long" + }, + "target_path": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "flow": { + "properties": { + "complete": { + "type": "boolean" + }, + "final": { + "type": "boolean" + } + } + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "geoip": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "properties": { + "blake2b_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "blake2b_384": { + "ignore_above": 1024, + "type": "keyword" + }, + "blake2b_512": { + "ignore_above": 1024, + "type": "keyword" + }, + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_512": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512_224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "xxh64": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "containerized": { + "type": "boolean" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "build": { + "ignore_above": 1024, + "type": "keyword" + }, + "codename": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "http": { + "properties": { + "request": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "method": { + "ignore_above": 1024, + "type": "keyword" + }, + "referrer": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "response": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "status_code": { + "type": "long" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "jolokia": { + "properties": { + "agent": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "secured": { + "type": "boolean" + }, + "server": { + "properties": { + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "vendor": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "kubernetes": { + "properties": { + "annotations": { + "type": "object" + }, + "container": { + "properties": { + "image": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "deployment": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "type": "object" + }, + "namespace": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pod": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "replicaset": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "statefulset": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "labels": { + "type": "object" + }, + "log": { + "properties": { + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "message": { + "norms": false, + "type": "text" + }, + "network": { + "properties": { + "application": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "type": "long" + }, + "community_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "forwarded_ip": { + "type": "ip" + }, + "iana_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "packets": { + "type": "long" + }, + "protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "transport": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "observer": { + "properties": { + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "vendor": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "organization": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "process": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "sha1": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + } + } + }, + "title": { + "ignore_above": 1024, + "type": "keyword" + }, + "working_directory": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "related": { + "properties": { + "ip": { + "type": "ip" + } + } + }, + "server": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "user": { + "properties": { + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "service": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "socket": { + "properties": { + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "source": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "packets": { + "type": "long" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "user": { + "properties": { + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "system": { + "properties": { + "audit": { + "properties": { + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "boottime": { + "type": "date" + }, + "containerized": { + "type": "boolean" + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "codename": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "timezone": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "offset": { + "properties": { + "sec": { + "type": "long" + } + } + } + } + }, + "uptime": { + "type": "long" + } + } + }, + "newsocket": { + "properties": { + "egid": { + "type": "long" + }, + "euid": { + "type": "long" + }, + "gid": { + "type": "long" + }, + "internal_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "kernel_sock_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "type": "long" + } + } + }, + "package": { + "properties": { + "arch": { + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "installtime": { + "type": "date" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "release": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "summary": { + "ignore_above": 1024, + "type": "keyword" + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "socket": { + "properties": { + "egid": { + "type": "long" + }, + "euid": { + "type": "long" + }, + "gid": { + "type": "long" + }, + "internal_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "kernel_sock_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "type": "long" + } + } + }, + "user": { + "properties": { + "dir": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "password": { + "properties": { + "last_changed": { + "type": "date" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "shell": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "user_information": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "tags": { + "ignore_above": 1024, + "type": "keyword" + }, + "url": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "fragment": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "ignore_above": 1024, + "type": "keyword" + }, + "password": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "query": { + "ignore_above": 1024, + "type": "keyword" + }, + "scheme": { + "ignore_above": 1024, + "type": "keyword" + }, + "username": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user": { + "properties": { + "audit": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "effective": { + "properties": { + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "filesystem": { + "properties": { + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "full_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "name_map": { + "type": "object" + }, + "saved": { + "properties": { + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "selinux": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "role": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "terminal": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user_agent": { + "properties": { + "device": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "lifecycle": { + "indexing_complete": "true", + "name": "auditbeat-8.0.0", + "rollover_alias": "auditbeat-8.0.0" + }, + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "number_of_replicas": "0", + "number_of_shards": "1", + "query": { + "default_field": [ + "message", + "tags", + "agent.ephemeral_id", + "agent.id", + "agent.name", + "agent.type", + "agent.version", + "client.address", + "client.domain", + "client.geo.city_name", + "client.geo.continent_name", + "client.geo.country_iso_code", + "client.geo.country_name", + "client.geo.name", + "client.geo.region_iso_code", + "client.geo.region_name", + "client.mac", + "client.user.email", + "client.user.full_name", + "client.user.group.id", + "client.user.group.name", + "client.user.hash", + "client.user.id", + "client.user.name", + "cloud.account.id", + "cloud.availability_zone", + "cloud.instance.id", + "cloud.instance.name", + "cloud.machine.type", + "cloud.provider", + "cloud.region", + "container.id", + "container.image.name", + "container.image.tag", + "container.name", + "container.runtime", + "destination.address", + "destination.domain", + "destination.geo.city_name", + "destination.geo.continent_name", + "destination.geo.country_iso_code", + "destination.geo.country_name", + "destination.geo.name", + "destination.geo.region_iso_code", + "destination.geo.region_name", + "destination.mac", + "destination.user.email", + "destination.user.full_name", + "destination.user.group.id", + "destination.user.group.name", + "destination.user.hash", + "destination.user.id", + "destination.user.name", + "ecs.version", + "error.code", + "error.id", + "error.message", + "event.action", + "event.category", + "event.dataset", + "event.hash", + "event.id", + "event.kind", + "event.module", + "event.original", + "event.outcome", + "event.timezone", + "event.type", + "file.device", + "file.extension", + "file.gid", + "file.group", + "file.inode", + "file.mode", + "file.owner", + "file.path", + "file.target_path", + "file.type", + "file.uid", + "geo.city_name", + "geo.continent_name", + "geo.country_iso_code", + "geo.country_name", + "geo.name", + "geo.region_iso_code", + "geo.region_name", + "group.id", + "group.name", + "host.architecture", + "host.geo.city_name", + "host.geo.continent_name", + "host.geo.country_iso_code", + "host.geo.country_name", + "host.geo.name", + "host.geo.region_iso_code", + "host.geo.region_name", + "host.hostname", + "host.id", + "host.mac", + "host.name", + "host.os.family", + "host.os.full", + "host.os.kernel", + "host.os.name", + "host.os.platform", + "host.os.version", + "host.type", + "host.user.email", + "host.user.full_name", + "host.user.group.id", + "host.user.group.name", + "host.user.hash", + "host.user.id", + "host.user.name", + "http.request.body.content", + "http.request.method", + "http.request.referrer", + "http.response.body.content", + "http.version", + "log.level", + "log.original", + "network.application", + "network.community_id", + "network.direction", + "network.iana_number", + "network.name", + "network.protocol", + "network.transport", + "network.type", + "observer.geo.city_name", + "observer.geo.continent_name", + "observer.geo.country_iso_code", + "observer.geo.country_name", + "observer.geo.name", + "observer.geo.region_iso_code", + "observer.geo.region_name", + "observer.hostname", + "observer.mac", + "observer.os.family", + "observer.os.full", + "observer.os.kernel", + "observer.os.name", + "observer.os.platform", + "observer.os.version", + "observer.serial_number", + "observer.type", + "observer.vendor", + "observer.version", + "organization.id", + "organization.name", + "os.family", + "os.full", + "os.kernel", + "os.name", + "os.platform", + "os.version", + "process.args", + "process.executable", + "process.name", + "process.title", + "process.working_directory", + "server.address", + "server.domain", + "server.geo.city_name", + "server.geo.continent_name", + "server.geo.country_iso_code", + "server.geo.country_name", + "server.geo.name", + "server.geo.region_iso_code", + "server.geo.region_name", + "server.mac", + "server.user.email", + "server.user.full_name", + "server.user.group.id", + "server.user.group.name", + "server.user.hash", + "server.user.id", + "server.user.name", + "service.ephemeral_id", + "service.id", + "service.name", + "service.state", + "service.type", + "service.version", + "source.address", + "source.domain", + "source.geo.city_name", + "source.geo.continent_name", + "source.geo.country_iso_code", + "source.geo.country_name", + "source.geo.name", + "source.geo.region_iso_code", + "source.geo.region_name", + "source.mac", + "source.user.email", + "source.user.full_name", + "source.user.group.id", + "source.user.group.name", + "source.user.hash", + "source.user.id", + "source.user.name", + "url.domain", + "url.fragment", + "url.full", + "url.original", + "url.password", + "url.path", + "url.query", + "url.scheme", + "url.username", + "user.email", + "user.full_name", + "user.group.id", + "user.group.name", + "user.hash", + "user.id", + "user.name", + "user_agent.device.name", + "user_agent.name", + "user_agent.original", + "user_agent.os.family", + "user_agent.os.full", + "user_agent.os.kernel", + "user_agent.os.name", + "user_agent.os.platform", + "user_agent.os.version", + "user_agent.version", + "agent.hostname", + "error.type", + "cloud.project.id", + "host.os.build", + "kubernetes.pod.name", + "kubernetes.pod.uid", + "kubernetes.namespace", + "kubernetes.node.name", + "kubernetes.replicaset.name", + "kubernetes.deployment.name", + "kubernetes.statefulset.name", + "kubernetes.container.name", + "kubernetes.container.image", + "jolokia.agent.version", + "jolokia.agent.id", + "jolokia.server.product", + "jolokia.server.version", + "jolokia.server.vendor", + "jolokia.url", + "raw", + "file.origin", + "file.selinux.user", + "file.selinux.role", + "file.selinux.domain", + "file.selinux.level", + "user.audit.id", + "user.audit.name", + "user.effective.id", + "user.effective.name", + "user.effective.group.id", + "user.effective.group.name", + "user.filesystem.id", + "user.filesystem.name", + "user.filesystem.group.id", + "user.filesystem.group.name", + "user.saved.id", + "user.saved.name", + "user.saved.group.id", + "user.saved.group.name", + "user.selinux.user", + "user.selinux.role", + "user.selinux.domain", + "user.selinux.level", + "user.selinux.category", + "source.path", + "destination.path", + "auditd.message_type", + "auditd.session", + "auditd.result", + "auditd.summary.actor.primary", + "auditd.summary.actor.secondary", + "auditd.summary.object.type", + "auditd.summary.object.primary", + "auditd.summary.object.secondary", + "auditd.summary.how", + "auditd.paths.inode", + "auditd.paths.dev", + "auditd.paths.obj_user", + "auditd.paths.obj_role", + "auditd.paths.obj_domain", + "auditd.paths.obj_level", + "auditd.paths.objtype", + "auditd.paths.ouid", + "auditd.paths.rdev", + "auditd.paths.nametype", + "auditd.paths.ogid", + "auditd.paths.item", + "auditd.paths.mode", + "auditd.paths.name", + "auditd.data.action", + "auditd.data.minor", + "auditd.data.acct", + "auditd.data.addr", + "auditd.data.cipher", + "auditd.data.id", + "auditd.data.entries", + "auditd.data.kind", + "auditd.data.ksize", + "auditd.data.spid", + "auditd.data.arch", + "auditd.data.argc", + "auditd.data.major", + "auditd.data.unit", + "auditd.data.table", + "auditd.data.terminal", + "auditd.data.grantors", + "auditd.data.direction", + "auditd.data.op", + "auditd.data.tty", + "auditd.data.syscall", + "auditd.data.data", + "auditd.data.family", + "auditd.data.mac", + "auditd.data.pfs", + "auditd.data.items", + "auditd.data.a0", + "auditd.data.a1", + "auditd.data.a2", + "auditd.data.a3", + "auditd.data.hostname", + "auditd.data.lport", + "auditd.data.rport", + "auditd.data.exit", + "auditd.data.fp", + "auditd.data.laddr", + "auditd.data.sport", + "auditd.data.capability", + "auditd.data.nargs", + "auditd.data.new-enabled", + "auditd.data.audit_backlog_limit", + "auditd.data.dir", + "auditd.data.cap_pe", + "auditd.data.model", + "auditd.data.new_pp", + "auditd.data.old-enabled", + "auditd.data.oauid", + "auditd.data.old", + "auditd.data.banners", + "auditd.data.feature", + "auditd.data.vm-ctx", + "auditd.data.opid", + "auditd.data.seperms", + "auditd.data.seresult", + "auditd.data.new-rng", + "auditd.data.old-net", + "auditd.data.sigev_signo", + "auditd.data.ino", + "auditd.data.old_enforcing", + "auditd.data.old-vcpu", + "auditd.data.range", + "auditd.data.res", + "auditd.data.added", + "auditd.data.fam", + "auditd.data.nlnk-pid", + "auditd.data.subj", + "auditd.data.a[0-3]", + "auditd.data.cgroup", + "auditd.data.kernel", + "auditd.data.ocomm", + "auditd.data.new-net", + "auditd.data.permissive", + "auditd.data.class", + "auditd.data.compat", + "auditd.data.fi", + "auditd.data.changed", + "auditd.data.msg", + "auditd.data.dport", + "auditd.data.new-seuser", + "auditd.data.invalid_context", + "auditd.data.dmac", + "auditd.data.ipx-net", + "auditd.data.iuid", + "auditd.data.macproto", + "auditd.data.obj", + "auditd.data.ipid", + "auditd.data.new-fs", + "auditd.data.vm-pid", + "auditd.data.cap_pi", + "auditd.data.old-auid", + "auditd.data.oses", + "auditd.data.fd", + "auditd.data.igid", + "auditd.data.new-disk", + "auditd.data.parent", + "auditd.data.len", + "auditd.data.oflag", + "auditd.data.uuid", + "auditd.data.code", + "auditd.data.nlnk-grp", + "auditd.data.cap_fp", + "auditd.data.new-mem", + "auditd.data.seperm", + "auditd.data.enforcing", + "auditd.data.new-chardev", + "auditd.data.old-rng", + "auditd.data.outif", + "auditd.data.cmd", + "auditd.data.hook", + "auditd.data.new-level", + "auditd.data.sauid", + "auditd.data.sig", + "auditd.data.audit_backlog_wait_time", + "auditd.data.printer", + "auditd.data.old-mem", + "auditd.data.perm", + "auditd.data.old_pi", + "auditd.data.state", + "auditd.data.format", + "auditd.data.new_gid", + "auditd.data.tcontext", + "auditd.data.maj", + "auditd.data.watch", + "auditd.data.device", + "auditd.data.grp", + "auditd.data.bool", + "auditd.data.icmp_type", + "auditd.data.new_lock", + "auditd.data.old_prom", + "auditd.data.acl", + "auditd.data.ip", + "auditd.data.new_pi", + "auditd.data.default-context", + "auditd.data.inode_gid", + "auditd.data.new-log_passwd", + "auditd.data.new_pe", + "auditd.data.selected-context", + "auditd.data.cap_fver", + "auditd.data.file", + "auditd.data.net", + "auditd.data.virt", + "auditd.data.cap_pp", + "auditd.data.old-range", + "auditd.data.resrc", + "auditd.data.new-range", + "auditd.data.obj_gid", + "auditd.data.proto", + "auditd.data.old-disk", + "auditd.data.audit_failure", + "auditd.data.inif", + "auditd.data.vm", + "auditd.data.flags", + "auditd.data.nlnk-fam", + "auditd.data.old-fs", + "auditd.data.old-ses", + "auditd.data.seqno", + "auditd.data.fver", + "auditd.data.qbytes", + "auditd.data.seuser", + "auditd.data.cap_fe", + "auditd.data.new-vcpu", + "auditd.data.old-level", + "auditd.data.old_pp", + "auditd.data.daddr", + "auditd.data.old-role", + "auditd.data.ioctlcmd", + "auditd.data.smac", + "auditd.data.apparmor", + "auditd.data.fe", + "auditd.data.perm_mask", + "auditd.data.ses", + "auditd.data.cap_fi", + "auditd.data.obj_uid", + "auditd.data.reason", + "auditd.data.list", + "auditd.data.old_lock", + "auditd.data.bus", + "auditd.data.old_pe", + "auditd.data.new-role", + "auditd.data.prom", + "auditd.data.uri", + "auditd.data.audit_enabled", + "auditd.data.old-log_passwd", + "auditd.data.old-seuser", + "auditd.data.per", + "auditd.data.scontext", + "auditd.data.tclass", + "auditd.data.ver", + "auditd.data.new", + "auditd.data.val", + "auditd.data.img-ctx", + "auditd.data.old-chardev", + "auditd.data.old_val", + "auditd.data.success", + "auditd.data.inode_uid", + "auditd.data.removed", + "auditd.data.socket.port", + "auditd.data.socket.saddr", + "auditd.data.socket.addr", + "auditd.data.socket.family", + "auditd.data.socket.path", + "geoip.continent_name", + "geoip.city_name", + "geoip.region_name", + "geoip.country_iso_code", + "hash.blake2b_256", + "hash.blake2b_384", + "hash.blake2b_512", + "hash.md5", + "hash.sha1", + "hash.sha224", + "hash.sha256", + "hash.sha384", + "hash.sha3_224", + "hash.sha3_256", + "hash.sha3_384", + "hash.sha3_512", + "hash.sha512", + "hash.sha512_224", + "hash.sha512_256", + "hash.xxh64", + "event.origin", + "user.entity_id", + "user.terminal", + "process.entity_id", + "socket.entity_id", + "system.audit.host.timezone.name", + "system.audit.host.hostname", + "system.audit.host.id", + "system.audit.host.architecture", + "system.audit.host.mac", + "system.audit.host.os.platform", + "system.audit.host.os.name", + "system.audit.host.os.family", + "system.audit.host.os.version", + "system.audit.host.os.kernel", + "system.audit.package.entity_id", + "system.audit.package.name", + "system.audit.package.version", + "system.audit.package.release", + "system.audit.package.arch", + "system.audit.package.license", + "system.audit.package.summary", + "system.audit.package.url", + "system.audit.user.name", + "system.audit.user.uid", + "system.audit.user.gid", + "system.audit.user.dir", + "system.audit.user.shell", + "system.audit.user.user_information", + "system.audit.user.password.type", + "fields.*" + ] + }, + "refresh_interval": "5s" + } + } + } +} diff --git a/x-pack/test/security_solution_cypress/es_archives/threat_indicator/data.json b/x-pack/test/security_solution_cypress/es_archives/threat_indicator/data.json new file mode 100644 index 0000000000000..dfe0444e0bbd4 --- /dev/null +++ b/x-pack/test/security_solution_cypress/es_archives/threat_indicator/data.json @@ -0,0 +1,13 @@ +{ + "type": "doc", + "value": { + "id": "_uZE6nwBOpWiDweSth_D", + "index": "threat-indicator-0001", + "source": { + "@timestamp": "2019-09-01T00:41:06.527Z", + "agent": { + "threat": "03ccb0ce-f65c-4279-a619-05f1d5bb000b" + } + } + } +} diff --git a/x-pack/test/security_solution_cypress/es_archives/threat_indicator/mappings.json b/x-pack/test/security_solution_cypress/es_archives/threat_indicator/mappings.json new file mode 100644 index 0000000000000..0c24fa429d908 --- /dev/null +++ b/x-pack/test/security_solution_cypress/es_archives/threat_indicator/mappings.json @@ -0,0 +1,30 @@ +{ + "type": "index", + "value": { + "aliases": { + "threat-indicator": { + "is_write_index": false + } + }, + "index": "threat-indicator-0001", + "mappings": { + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "threat": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } +} From bb7698958252d60724d542edaa7cf54e47cfbc21 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Thu, 3 Dec 2020 11:51:56 +0100 Subject: [PATCH 40/40] =?UTF-8?q?fix:=20=F0=9F=90=9B=20don't=20add=20separ?= =?UTF-8?q?ator=20befor=20group=20on=20no=20main=20items=20(#83166)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../build_eui_context_menu_panels.test.ts | 202 +++++++++++++++++- .../build_eui_context_menu_panels.tsx | 10 +- 2 files changed, 207 insertions(+), 5 deletions(-) diff --git a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.test.ts b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.test.ts index 3a598b547e343..3111a0b55084c 100644 --- a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.test.ts +++ b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.test.ts @@ -20,25 +20,31 @@ import { EuiContextMenuPanelDescriptor } from '@elastic/eui'; import { buildContextMenuForActions } from './build_eui_context_menu_panels'; import { Action, createAction } from '../actions'; +import { PresentableGrouping } from '../util'; const createTestAction = ({ type, dispayName, order, + grouping = undefined, }: { type?: string; dispayName: string; order?: number; + grouping?: PresentableGrouping; }) => createAction({ type: type as any, // mapping doesn't matter for this test getDisplayName: () => dispayName, order, execute: async () => {}, + grouping, }); const resultMapper = (panel: EuiContextMenuPanelDescriptor) => ({ - items: panel.items ? panel.items.map((item) => ({ name: item.name })) : [], + items: panel.items + ? panel.items.map((item) => ({ name: item.isSeparator ? 'SEPARATOR' : item.name })) + : [], }); test('sorts items in DESC order by "order" field first, then by display name', async () => { @@ -237,3 +243,197 @@ test('hides items behind in "More" submenu if there are more than 4 actions', as ] `); }); + +test('separates grouped items from main items with a separator', async () => { + const actions = [ + createTestAction({ + dispayName: 'Foo 1', + }), + createTestAction({ + dispayName: 'Foo 2', + }), + createTestAction({ + dispayName: 'Foo 3', + }), + createTestAction({ + dispayName: 'Foo 4', + grouping: [ + { + id: 'testGroup', + getDisplayName: () => 'Test group', + }, + ], + }), + ]; + const menu = await buildContextMenuForActions({ + actions: actions.map((action) => ({ action, context: {}, trigger: 'TEST' as any })), + }); + + expect(menu.map(resultMapper)).toMatchInlineSnapshot(` + Array [ + Object { + "items": Array [ + Object { + "name": "Foo 1", + }, + Object { + "name": "Foo 2", + }, + Object { + "name": "Foo 3", + }, + Object { + "name": "SEPARATOR", + }, + Object { + "name": "Foo 4", + }, + ], + }, + Object { + "items": Array [ + Object { + "name": "Foo 4", + }, + ], + }, + ] + `); +}); + +test('separates multiple groups each with its own separator', async () => { + const actions = [ + createTestAction({ + dispayName: 'Foo 1', + }), + createTestAction({ + dispayName: 'Foo 2', + }), + createTestAction({ + dispayName: 'Foo 3', + }), + createTestAction({ + dispayName: 'Foo 4', + grouping: [ + { + id: 'testGroup', + getDisplayName: () => 'Test group', + }, + ], + }), + createTestAction({ + dispayName: 'Foo 5', + grouping: [ + { + id: 'testGroup2', + getDisplayName: () => 'Test group 2', + }, + ], + }), + ]; + const menu = await buildContextMenuForActions({ + actions: actions.map((action) => ({ action, context: {}, trigger: 'TEST' as any })), + }); + + expect(menu.map(resultMapper)).toMatchInlineSnapshot(` + Array [ + Object { + "items": Array [ + Object { + "name": "Foo 1", + }, + Object { + "name": "Foo 2", + }, + Object { + "name": "Foo 3", + }, + Object { + "name": "SEPARATOR", + }, + Object { + "name": "Foo 4", + }, + Object { + "name": "SEPARATOR", + }, + Object { + "name": "Foo 5", + }, + ], + }, + Object { + "items": Array [ + Object { + "name": "Foo 4", + }, + ], + }, + Object { + "items": Array [ + Object { + "name": "Foo 5", + }, + ], + }, + ] + `); +}); + +test('does not add separator for first grouping if there are no main items', async () => { + const actions = [ + createTestAction({ + dispayName: 'Foo 4', + grouping: [ + { + id: 'testGroup', + getDisplayName: () => 'Test group', + }, + ], + }), + createTestAction({ + dispayName: 'Foo 5', + grouping: [ + { + id: 'testGroup2', + getDisplayName: () => 'Test group 2', + }, + ], + }), + ]; + const menu = await buildContextMenuForActions({ + actions: actions.map((action) => ({ action, context: {}, trigger: 'TEST' as any })), + }); + + expect(menu.map(resultMapper)).toMatchInlineSnapshot(` + Array [ + Object { + "items": Array [ + Object { + "name": "Foo 4", + }, + Object { + "name": "SEPARATOR", + }, + Object { + "name": "Foo 5", + }, + ], + }, + Object { + "items": Array [ + Object { + "name": "Foo 4", + }, + ], + }, + Object { + "items": Array [ + Object { + "name": "Foo 5", + }, + ], + }, + ] + `); +}); diff --git a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx index c7efb6dad326d..63586ca3da1f7 100644 --- a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx +++ b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx @@ -201,10 +201,12 @@ export async function buildContextMenuForActions({ for (const panel of Object.values(panels)) { if (panel._level === 0) { - panels.mainMenu.items.push({ - isSeparator: true, - key: panel.id + '__separator', - }); + if (panels.mainMenu.items.length > 0) { + panels.mainMenu.items.push({ + isSeparator: true, + key: panel.id + '__separator', + }); + } if (panel.items.length > 3) { panels.mainMenu.items.push({ name: panel.title || panel.id,

    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

ubhg?P)CP3h(yuB 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̙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