diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/index.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/index.ts index 1d1df54d62f70..479770a9d0a81 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/index.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/index.ts @@ -5,8 +5,9 @@ */ import { functions as commonFunctions } from '../common'; +import { functions as externalFunctions } from '../external'; import { location } from './location'; import { markdown } from './markdown'; import { urlparam } from './urlparam'; -export const functions = [location, markdown, urlparam, ...commonFunctions]; +export const functions = [location, markdown, urlparam, ...commonFunctions, ...externalFunctions]; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/index.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/index.ts index 36fa6497ab6f3..79538941bbbfa 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/index.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/index.ts @@ -48,10 +48,6 @@ import { rounddate } from './rounddate'; import { rowCount } from './rowCount'; import { repeatImage } from './repeatImage'; import { revealImage } from './revealImage'; -import { savedLens } from './saved_lens'; -import { savedMap } from './saved_map'; -import { savedSearch } from './saved_search'; -import { savedVisualization } from './saved_visualization'; import { seriesStyle } from './seriesStyle'; import { shape } from './shape'; import { sort } from './sort'; @@ -110,10 +106,6 @@ export const functions = [ revealImage, rounddate, rowCount, - savedLens, - savedMap, - savedSearch, - savedVisualization, seriesStyle, shape, sort, diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/index.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/index.ts new file mode 100644 index 0000000000000..17682c5a72074 --- /dev/null +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/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 { savedLens } from './saved_lens'; +import { savedMap } from './saved_map'; +import { savedSearch } from './saved_search'; +import { savedVisualization } from './saved_visualization'; + +export const functions = [savedLens, savedMap, savedSearch, savedVisualization]; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/saved_lens.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.test.ts similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/functions/common/saved_lens.test.ts rename to x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.test.ts diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/saved_lens.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.ts similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/functions/common/saved_lens.ts rename to x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.ts diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/saved_map.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_map.test.ts similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/functions/common/saved_map.test.ts rename to x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_map.test.ts diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/saved_map.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_map.ts similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/functions/common/saved_map.ts rename to x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_map.ts diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/saved_search.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_search.test.ts similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/functions/common/saved_search.test.ts rename to x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_search.test.ts diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/saved_search.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_search.ts similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/functions/common/saved_search.ts rename to x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_search.ts diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_visualization.test.ts similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.test.ts rename to x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_visualization.test.ts diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_visualization.ts similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts rename to x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_visualization.ts diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/core.js b/x-pack/plugins/canvas/canvas_plugin_src/renderers/core.js new file mode 100644 index 0000000000000..ba4a564848788 --- /dev/null +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/core.js @@ -0,0 +1,37 @@ +/* + * 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 { debug } from './debug'; +import { error } from './error'; +import { image } from './image'; +import { markdown } from './markdown'; +import { metric } from './metric'; +import { pie } from './pie'; +import { plot } from './plot'; +import { progress } from './progress'; +import { repeatImage } from './repeat_image'; +import { revealImage } from './reveal_image'; +import { shape } from './shape'; +import { table } from './table'; +import { text } from './text'; + +export const renderFunctions = [ + debug, + error, + image, + markdown, + metric, + pie, + plot, + progress, + repeatImage, + revealImage, + shape, + table, + text, +]; + +export const renderFunctionFactories = []; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/index.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/index.ts new file mode 100644 index 0000000000000..fb47d61484c47 --- /dev/null +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/index.ts @@ -0,0 +1,9 @@ +/* + * 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 { embeddableRendererFactory } from './embeddable'; + +export const renderFunctions = []; +export const renderFunctionFactories = [embeddableRendererFactory]; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.test.ts index c4a9a22be3202..7bcfd6bef4620 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.test.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.test.ts @@ -5,7 +5,7 @@ */ import { toExpression } from './lens'; -import { SavedLensInput } from '../../../functions/common/saved_lens'; +import { SavedLensInput } from '../../../functions/external/saved_lens'; import { fromExpression, Ast } from '@kbn/interpreter/common'; const baseEmbeddableInput = { diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.ts index 445cb7480ff80..5bb45c5ca129e 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedLensInput } from '../../../functions/common/saved_lens'; +import { SavedLensInput } from '../../../functions/external/saved_lens'; export function toExpression(input: SavedLensInput): string { const expressionParts = [] as string[]; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/advanced_filter/component/__examples__/__snapshots__/advanced_filter.stories.storyshot b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/advanced_filter/component/__examples__/__snapshots__/advanced_filter.stories.storyshot similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/advanced_filter/component/__examples__/__snapshots__/advanced_filter.stories.storyshot rename to x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/advanced_filter/component/__examples__/__snapshots__/advanced_filter.stories.storyshot diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/advanced_filter/component/__examples__/advanced_filter.stories.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/advanced_filter/component/__examples__/advanced_filter.stories.tsx similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/advanced_filter/component/__examples__/advanced_filter.stories.tsx rename to x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/advanced_filter/component/__examples__/advanced_filter.stories.tsx diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/advanced_filter/component/advanced_filter.scss b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/advanced_filter/component/advanced_filter.scss similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/advanced_filter/component/advanced_filter.scss rename to x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/advanced_filter/component/advanced_filter.scss diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/advanced_filter/component/advanced_filter.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/advanced_filter/component/advanced_filter.tsx similarity index 96% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/advanced_filter/component/advanced_filter.tsx rename to x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/advanced_filter/component/advanced_filter.tsx index e4d4510d40f53..8cb8b53ab6976 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/advanced_filter/component/advanced_filter.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/advanced_filter/component/advanced_filter.tsx @@ -7,7 +7,7 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import PropTypes from 'prop-types'; import React, { FunctionComponent } from 'react'; -import { ComponentStrings } from '../../../../i18n'; +import { ComponentStrings } from '../../../../../i18n'; const { AdvancedFilter: strings } = ComponentStrings; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/advanced_filter/component/index.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/advanced_filter/component/index.ts similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/advanced_filter/component/index.ts rename to x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/advanced_filter/component/index.ts diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/advanced_filter/index.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/advanced_filter/index.tsx similarity index 89% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/advanced_filter/index.tsx rename to x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/advanced_filter/index.tsx index 29692b0c02a41..b481a4b498928 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/advanced_filter/index.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/advanced_filter/index.tsx @@ -6,9 +6,9 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { RendererFactory } from '../../../types'; +import { RendererFactory } from '../../../../types'; import { AdvancedFilter } from './component'; -import { RendererStrings } from '../../../i18n'; +import { RendererStrings } from '../../../../i18n'; const { advancedFilter: strings } = RendererStrings; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/dropdown_filter/component/__examples__/__snapshots__/dropdown_filter.stories.storyshot b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/dropdown_filter/component/__examples__/__snapshots__/dropdown_filter.stories.storyshot similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/dropdown_filter/component/__examples__/__snapshots__/dropdown_filter.stories.storyshot rename to x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/dropdown_filter/component/__examples__/__snapshots__/dropdown_filter.stories.storyshot diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/dropdown_filter/component/__examples__/dropdown_filter.stories.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/dropdown_filter/component/__examples__/dropdown_filter.stories.tsx similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/dropdown_filter/component/__examples__/dropdown_filter.stories.tsx rename to x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/dropdown_filter/component/__examples__/dropdown_filter.stories.tsx diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/dropdown_filter/component/dropdown_filter.scss b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/dropdown_filter/component/dropdown_filter.scss similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/dropdown_filter/component/dropdown_filter.scss rename to x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/dropdown_filter/component/dropdown_filter.scss diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/dropdown_filter/component/dropdown_filter.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/dropdown_filter/component/dropdown_filter.tsx similarity index 97% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/dropdown_filter/component/dropdown_filter.tsx rename to x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/dropdown_filter/component/dropdown_filter.tsx index 9cade90bd5870..ac08d3dd25d85 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/dropdown_filter/component/dropdown_filter.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/dropdown_filter/component/dropdown_filter.tsx @@ -7,7 +7,7 @@ import { EuiIcon } from '@elastic/eui'; import PropTypes from 'prop-types'; import React, { ChangeEvent, FocusEvent, FunctionComponent } from 'react'; -import { ComponentStrings } from '../../../../i18n'; +import { ComponentStrings } from '../../../../../i18n'; const { DropdownFilter: strings } = ComponentStrings; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/dropdown_filter/component/index.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/dropdown_filter/component/index.ts similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/dropdown_filter/component/index.ts rename to x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/dropdown_filter/component/index.ts diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/dropdown_filter/index.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/dropdown_filter/index.tsx similarity index 93% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/dropdown_filter/index.tsx rename to x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/dropdown_filter/index.tsx index ee07f7d4402f4..bfc36932a8a07 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/dropdown_filter/index.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/dropdown_filter/index.tsx @@ -8,10 +8,10 @@ import { fromExpression, toExpression, Ast } from '@kbn/interpreter/common'; import { get } from 'lodash'; import React from 'react'; import ReactDOM from 'react-dom'; -import { syncFilterExpression } from '../../../public/lib/sync_filter_expression'; -import { RendererFactory } from '../../../types'; +import { syncFilterExpression } from '../../../../public/lib/sync_filter_expression'; +import { RendererFactory } from '../../../../types'; import { DropdownFilter } from './component'; -import { RendererStrings } from '../../../i18n'; +import { RendererStrings } from '../../../../i18n'; const { dropdownFilter: strings } = RendererStrings; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/index.js b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/index.js new file mode 100644 index 0000000000000..0654bf0f704e9 --- /dev/null +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/index.js @@ -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 { advancedFilter } from './advanced_filter'; +import { dropdownFilter } from './dropdown_filter'; +import { timeFilterFactory } from './time_filter'; + +export const renderFunctions = [advancedFilter, dropdownFilter]; +export const renderFunctionFactories = [timeFilterFactory]; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/time_filter/components/__examples__/__snapshots__/time_filter.stories.storyshot b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/time_filter/components/__examples__/__snapshots__/time_filter.stories.storyshot similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/time_filter/components/__examples__/__snapshots__/time_filter.stories.storyshot rename to x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/time_filter/components/__examples__/__snapshots__/time_filter.stories.storyshot diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/time_filter/components/__examples__/time_filter.stories.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/time_filter/components/__examples__/time_filter.stories.tsx similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/time_filter/components/__examples__/time_filter.stories.tsx rename to x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/time_filter/components/__examples__/time_filter.stories.tsx diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/time_filter/components/index.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/time_filter/components/index.tsx similarity index 78% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/time_filter/components/index.tsx rename to x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/time_filter/components/index.tsx index 85ea754de670d..cdea7d6591592 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/time_filter/components/index.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/time_filter/components/index.tsx @@ -4,6 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -import { TimeFilter } from './time_filter'; - -export { TimeFilter }; +export { TimeFilter } from './time_filter'; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/time_filter/components/time_filter.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/time_filter/components/time_filter.tsx similarity index 98% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/time_filter/components/time_filter.tsx rename to x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/time_filter/components/time_filter.tsx index 487f17fb89d12..3cb192cedde83 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/time_filter/components/time_filter.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/time_filter/components/time_filter.tsx @@ -8,7 +8,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { get } from 'lodash'; import { fromExpression } from '@kbn/interpreter/common'; -import { UnitStrings } from '../../../../i18n/units'; +import { UnitStrings } from '../../../../../i18n/units'; const { quickRanges: strings } = UnitStrings; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/time_filter/index.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/time_filter/index.tsx similarity index 82% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/time_filter/index.tsx rename to x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/time_filter/index.tsx index 25278adcf4529..03bf18830a761 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/time_filter/index.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/time_filter/index.tsx @@ -7,14 +7,14 @@ import ReactDOM from 'react-dom'; import React from 'react'; import { toExpression } from '@kbn/interpreter/common'; -import { UI_SETTINGS } from '../../../../../../src/plugins/data/public'; -import { syncFilterExpression } from '../../../public/lib/sync_filter_expression'; -import { RendererStrings } from '../../../i18n'; +import { UI_SETTINGS } from '../../../../../../../src/plugins/data/public'; +import { syncFilterExpression } from '../../../../public/lib/sync_filter_expression'; +import { RendererStrings } from '../../../../i18n'; import { TimeFilter } from './components'; -import { StartInitializer } from '../../plugin'; -import { RendererHandlers } from '../../../types'; -import { Arguments } from '../../functions/common/timefilterControl'; -import { RendererFactory } from '../../../types'; +import { StartInitializer } from '../../../plugin'; +import { RendererHandlers } from '../../../../types'; +import { Arguments } from '../../../functions/common/timefilterControl'; +import { RendererFactory } from '../../../../types'; const { timeFilter: strings } = RendererStrings; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/time_filter/time_filter.scss b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/time_filter/time_filter.scss similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/time_filter/time_filter.scss rename to x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/time_filter/time_filter.scss diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/index.js b/x-pack/plugins/canvas/canvas_plugin_src/renderers/index.js index 263f2d8ec30b5..38f10afd50cf9 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/index.js +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/index.js @@ -4,40 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -import { advancedFilter } from './advanced_filter'; -import { debug } from './debug'; -import { dropdownFilter } from './dropdown_filter'; -import { embeddableRendererFactory } from './embeddable/embeddable'; -import { error } from './error'; -import { image } from './image'; -import { markdown } from './markdown'; -import { metric } from './metric'; -import { pie } from './pie'; -import { plot } from './plot'; -import { progress } from './progress'; -import { repeatImage } from './repeat_image'; -import { revealImage } from './reveal_image'; -import { shape } from './shape'; -import { table } from './table'; -import { text } from './text'; -import { timeFilterFactory } from './time_filter'; +import { + renderFunctions as embeddableFunctions, + renderFunctionFactories as embeddableFactories, +} from './embeddable'; -export const renderFunctions = [ - advancedFilter, - debug, - dropdownFilter, - error, - image, - markdown, - metric, - pie, - plot, - progress, - repeatImage, - revealImage, - shape, - table, - text, -]; +import { + renderFunctions as filterFunctions, + renderFunctionFactories as filterFactories, +} from './filters'; + +import { renderFunctions as coreFunctions, renderFunctionFactories as coreFactories } from './core'; -export const renderFunctionFactories = [embeddableRendererFactory, timeFilterFactory]; +export const renderFunctions = [...coreFunctions, ...filterFunctions, ...embeddableFunctions]; +export const renderFunctionFactories = [ + ...coreFactories, + ...embeddableFactories, + ...filterFactories, +]; diff --git a/x-pack/plugins/canvas/i18n/functions/dict/saved_lens.ts b/x-pack/plugins/canvas/i18n/functions/dict/saved_lens.ts index 1efcbc9d3a18e..e146a6ca68449 100644 --- a/x-pack/plugins/canvas/i18n/functions/dict/saved_lens.ts +++ b/x-pack/plugins/canvas/i18n/functions/dict/saved_lens.ts @@ -5,7 +5,7 @@ */ import { i18n } from '@kbn/i18n'; -import { savedLens } from '../../../canvas_plugin_src/functions/common/saved_lens'; +import { savedLens } from '../../../canvas_plugin_src/functions/external/saved_lens'; import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; diff --git a/x-pack/plugins/canvas/i18n/functions/dict/saved_map.ts b/x-pack/plugins/canvas/i18n/functions/dict/saved_map.ts index 53bcd481f185f..8615565897434 100644 --- a/x-pack/plugins/canvas/i18n/functions/dict/saved_map.ts +++ b/x-pack/plugins/canvas/i18n/functions/dict/saved_map.ts @@ -5,7 +5,7 @@ */ import { i18n } from '@kbn/i18n'; -import { savedMap } from '../../../canvas_plugin_src/functions/common/saved_map'; +import { savedMap } from '../../../canvas_plugin_src/functions/external/saved_map'; import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; diff --git a/x-pack/plugins/canvas/i18n/functions/dict/saved_search.ts b/x-pack/plugins/canvas/i18n/functions/dict/saved_search.ts index 718deea5df788..865a7753d9818 100644 --- a/x-pack/plugins/canvas/i18n/functions/dict/saved_search.ts +++ b/x-pack/plugins/canvas/i18n/functions/dict/saved_search.ts @@ -5,7 +5,7 @@ */ import { i18n } from '@kbn/i18n'; -import { savedSearch } from '../../../canvas_plugin_src/functions/common/saved_search'; +import { savedSearch } from '../../../canvas_plugin_src/functions/external/saved_search'; import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; diff --git a/x-pack/plugins/canvas/i18n/functions/dict/saved_visualization.ts b/x-pack/plugins/canvas/i18n/functions/dict/saved_visualization.ts index 21a2e1c1b8800..30f88b51e7576 100644 --- a/x-pack/plugins/canvas/i18n/functions/dict/saved_visualization.ts +++ b/x-pack/plugins/canvas/i18n/functions/dict/saved_visualization.ts @@ -5,7 +5,7 @@ */ import { i18n } from '@kbn/i18n'; -import { savedVisualization } from '../../../canvas_plugin_src/functions/common/saved_visualization'; +import { savedVisualization } from '../../../canvas_plugin_src/functions/external/saved_visualization'; import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; diff --git a/x-pack/plugins/canvas/public/style/index.scss b/x-pack/plugins/canvas/public/style/index.scss index 9cd2bdabd3f45..ccaf6f1c1a478 100644 --- a/x-pack/plugins/canvas/public/style/index.scss +++ b/x-pack/plugins/canvas/public/style/index.scss @@ -56,10 +56,10 @@ @import '../components/workpad_page/workpad_interactive_page/workpad_interactive_page'; @import '../components/workpad_page/workpad_static_page/workpad_static_page'; -@import '../../canvas_plugin_src/renderers/advanced_filter/component/advanced_filter.scss'; -@import '../../canvas_plugin_src/renderers/dropdown_filter/component/dropdown_filter.scss'; +@import '../../canvas_plugin_src/renderers/filters/advanced_filter/component/advanced_filter.scss'; +@import '../../canvas_plugin_src/renderers/filters/dropdown_filter/component/dropdown_filter.scss'; @import '../../canvas_plugin_src/renderers/embeddable/embeddable.scss'; @import '../../canvas_plugin_src/renderers/plot/plot.scss'; @import '../../canvas_plugin_src/renderers/reveal_image/reveal_image.scss'; -@import '../../canvas_plugin_src/renderers/time_filter/time_filter.scss'; +@import '../../canvas_plugin_src/renderers/filters/time_filter/time_filter.scss'; @import '../../canvas_plugin_src/uis/arguments/image_upload/image_upload.scss'; diff --git a/x-pack/plugins/canvas/types/functions.ts b/x-pack/plugins/canvas/types/functions.ts index afe5614ac6e5a..b995354e8e9d2 100644 --- a/x-pack/plugins/canvas/types/functions.ts +++ b/x-pack/plugins/canvas/types/functions.ts @@ -8,6 +8,7 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { functions as commonFunctions } from '../canvas_plugin_src/functions/common'; import { functions as browserFunctions } from '../canvas_plugin_src/functions/browser'; import { functions as serverFunctions } from '../canvas_plugin_src/functions/server'; +import { functions as externalFunctions } from '../canvas_plugin_src/functions/external'; import { initFunctions } from '../public/functions'; /** @@ -87,6 +88,7 @@ export type FunctionFactory = type CommonFunction = FunctionFactory; type BrowserFunction = FunctionFactory; type ServerFunction = FunctionFactory; +type ExternalFunction = FunctionFactory; type ClientFunctions = FunctionFactory< ReturnType extends Array ? U : never >; @@ -95,7 +97,12 @@ type ClientFunctions = FunctionFactory< * A collection of all Canvas Functions. */ -export type CanvasFunction = CommonFunction | BrowserFunction | ServerFunction | ClientFunctions; +export type CanvasFunction = + | CommonFunction + | BrowserFunction + | ServerFunction + | ExternalFunction + | ClientFunctions; /** * Represents a function called by the `case` Function. diff --git a/x-pack/plugins/lists/public/common/hooks/use_async.test.ts b/x-pack/plugins/lists/public/common/hooks/use_async.test.ts index 6f115929c3f67..33f28cfc97291 100644 --- a/x-pack/plugins/lists/public/common/hooks/use_async.test.ts +++ b/x-pack/plugins/lists/public/common/hooks/use_async.test.ts @@ -95,4 +95,36 @@ describe('useAsync', () => { expect(result.current.loading).toBe(false); }); + + it('multiple start calls reset state', async () => { + let resolve: (result: string) => void; + fn.mockImplementation(() => new Promise((_resolve) => (resolve = _resolve))); + + const { result, waitForNextUpdate } = renderHook(() => useAsync(fn)); + + act(() => { + result.current.start(args); + }); + + expect(result.current.loading).toBe(true); + + act(() => resolve('result')); + await waitForNextUpdate(); + + expect(result.current.loading).toBe(false); + expect(result.current.result).toBe('result'); + + act(() => { + result.current.start(args); + }); + + expect(result.current.loading).toBe(true); + expect(result.current.result).toBe(undefined); + + act(() => resolve('result')); + await waitForNextUpdate(); + + expect(result.current.loading).toBe(false); + expect(result.current.result).toBe('result'); + }); }); diff --git a/x-pack/plugins/lists/public/common/hooks/use_async.ts b/x-pack/plugins/lists/public/common/hooks/use_async.ts index 362cad069b7ea..2f4d611a4a73a 100644 --- a/x-pack/plugins/lists/public/common/hooks/use_async.ts +++ b/x-pack/plugins/lists/public/common/hooks/use_async.ts @@ -32,6 +32,8 @@ export const useAsync = ( const start = useCallback( (...args: Args) => { setLoading(true); + setResult(undefined); + setError(undefined); fn(...args) .then((r) => isMounted() && setResult(r)) .catch((e) => isMounted() && setError(e)) diff --git a/x-pack/plugins/security_solution/common/endpoint/constants.ts b/x-pack/plugins/security_solution/common/endpoint/constants.ts index e311e358e6146..6ea0c36328eed 100644 --- a/x-pack/plugins/security_solution/common/endpoint/constants.ts +++ b/x-pack/plugins/security_solution/common/endpoint/constants.ts @@ -9,3 +9,5 @@ export const alertsIndexPattern = 'logs-endpoint.alerts-*'; export const metadataIndexPattern = 'metrics-endpoint.metadata-*'; export const policyIndexPattern = 'metrics-endpoint.policy-*'; export const telemetryIndexPattern = 'metrics-endpoint.telemetry-*'; +export const LIMITED_CONCURRENCY_ENDPOINT_ROUTE_TAG = 'endpoint:limited-concurrency'; +export const LIMITED_CONCURRENCY_ENDPOINT_COUNT = 100; diff --git a/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/modal.tsx b/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/modal.tsx index d7d4be6d951b8..dc72260439090 100644 --- a/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/modal.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/modal.tsx @@ -46,6 +46,7 @@ export const ValueListsModalComponent: React.FC = ({ const { start: findLists, ...lists } = useFindLists(); const { start: deleteList, result: deleteResult } = useDeleteList(); const [exportListId, setExportListId] = useState(); + const [deletingListIds, setDeletingListIds] = useState([]); const { addError, addSuccess } = useAppToasts(); const fetchLists = useCallback(() => { @@ -54,16 +55,18 @@ export const ValueListsModalComponent: React.FC = ({ const handleDelete = useCallback( ({ id }: { id: string }) => { + setDeletingListIds([...deletingListIds, id]); deleteList({ http, id }); }, - [deleteList, http] + [deleteList, deletingListIds, http] ); useEffect(() => { - if (deleteResult != null) { + if (deleteResult != null && deletingListIds.length > 0) { + setDeletingListIds([...deletingListIds.filter((id) => id !== deleteResult.id)]); fetchLists(); } - }, [deleteResult, fetchLists]); + }, [deleteResult, deletingListIds, fetchLists]); const handleExport = useCallback( async ({ ids }: { ids: string[] }) => @@ -116,6 +119,12 @@ export const ValueListsModalComponent: React.FC = ({ return null; } + const tableItems = (lists.result?.data ?? []).map((item) => ({ + ...item, + isExporting: item.id === exportListId, + isDeleting: deletingListIds.includes(item.id), + })); + const pagination = { pageIndex, pageSize, @@ -133,7 +142,7 @@ export const ValueListsModalComponent: React.FC = ({ + lists.map((list) => ({ + ...list, + isDeleting: false, + isExporting: false, + })); describe('ValueListsTable', () => { it('renders a row for each list', () => { const lists = Array(3).fill(getListResponseMock()); + const items = buildItems(lists); const container = mount( { it('calls onChange when pagination is modified', () => { const lists = Array(6).fill(getListResponseMock()); + const items = buildItems(lists); const onChange = jest.fn(); const container = mount( { it('calls onExport when export is clicked', () => { const lists = Array(3).fill(getListResponseMock()); + const items = buildItems(lists); const onExport = jest.fn(); const container = mount( { it('calls onDelete when delete is clicked', () => { const lists = Array(3).fill(getListResponseMock()); + const items = buildItems(lists); const onDelete = jest.fn(); const container = mount( ; -type ActionCallback = (item: ListSchema) => void; +import { buildColumns } from './table_helpers'; +import { TableProps, TableItemCallback } from './types'; export interface ValueListsTableProps { - lists: TableProps['items']; + items: TableProps['items']; loading: boolean; onChange: TableProps['onChange']; - onExport: ActionCallback; - onDelete: ActionCallback; + onExport: TableItemCallback; + onDelete: TableItemCallback; pagination: Exclude; } -const buildColumns = ( - onExport: ActionCallback, - onDelete: ActionCallback -): TableProps['columns'] => [ - { - field: 'name', - name: i18n.COLUMN_FILE_NAME, - truncateText: true, - }, - { - field: 'created_at', - name: i18n.COLUMN_UPLOAD_DATE, - /* eslint-disable-next-line react/display-name */ - render: (value: ListSchema['created_at']) => ( - - ), - width: '30%', - }, - { - field: 'created_by', - name: i18n.COLUMN_CREATED_BY, - truncateText: true, - width: '20%', - }, - { - name: i18n.COLUMN_ACTIONS, - actions: [ - { - name: i18n.ACTION_EXPORT_NAME, - description: i18n.ACTION_EXPORT_DESCRIPTION, - icon: 'exportAction', - type: 'icon', - onClick: onExport, - 'data-test-subj': 'action-export-value-list', - }, - { - name: i18n.ACTION_DELETE_NAME, - description: i18n.ACTION_DELETE_DESCRIPTION, - icon: 'trash', - type: 'icon', - onClick: onDelete, - 'data-test-subj': 'action-delete-value-list', - }, - ], - width: '15%', - }, -]; - export const ValueListsTableComponent: React.FC = ({ - lists, + items, loading, onChange, onExport, @@ -87,7 +36,7 @@ export const ValueListsTableComponent: React.FC = ({ theme.eui.euiSizeXS}; + vertical-align: middle; +`; + +const ActionButton: React.FC<{ + content: string; + dataTestSubj: string; + icon: IconType; + isLoading: boolean; + item: TableItem; + onClick: TableItemCallback; +}> = ({ content, dataTestSubj, icon, item, onClick, isLoading }) => ( + + {isLoading ? ( + + ) : ( + onClick(item)} + /> + )} + +); + +export const buildColumns = ( + onExport: TableItemCallback, + onDelete: TableItemCallback +): TableProps['columns'] => [ + { + field: 'name', + name: i18n.COLUMN_FILE_NAME, + truncateText: true, + }, + { + field: 'created_at', + name: i18n.COLUMN_UPLOAD_DATE, + render: (value: ListSchema['created_at']) => ( + + ), + width: '30%', + }, + { + field: 'created_by', + name: i18n.COLUMN_CREATED_BY, + truncateText: true, + width: '20%', + }, + { + name: i18n.COLUMN_ACTIONS, + actions: [ + { + render: (item) => ( + + ), + }, + { + render: (item) => ( + + ), + }, + ], + width: '15%', + }, +]; diff --git a/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/types.ts b/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/types.ts new file mode 100644 index 0000000000000..f85e275247728 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/types.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiBasicTableProps } from '@elastic/eui'; + +import { ListSchema } from '../../../../../lists/common/schemas/response'; + +export interface TableItem extends ListSchema { + isDeleting: boolean; + isExporting: boolean; +} +export type TableProps = EuiBasicTableProps; +export type TableItemCallback = (item: TableItem) => void; diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/button/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/button/index.tsx index 5896a02b82023..c0a59fd07e348 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/button/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/button/index.tsx @@ -39,6 +39,7 @@ const Container = styled.div` } .${FLYOUT_BUTTON_CLASS_NAME} { + background: ${({ theme }) => rgba(theme.eui.euiPageBackgroundColor, 1)}; border-radius: 4px 4px 0 0; box-shadow: none; height: 46px; diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/index.tsx index f41d318ba9587..3f842bcc2eb68 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/index.tsx @@ -44,6 +44,8 @@ const StyledResizable = styled(Resizable)` const RESIZABLE_ENABLE = { left: true }; +const RESIZABLE_DISABLED = { left: false }; + const FlyoutPaneComponent: React.FC = ({ children, onClose, @@ -98,10 +100,10 @@ const FlyoutPaneComponent: React.FC = ({ size="l" > diff --git a/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.tsx index 9f20c7f6c1571..54b30aca44a1f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.tsx @@ -181,7 +181,7 @@ const GraphOverlayComponent = ({ {timelineId === TimelineId.active && timelineType === TimelineType.default && ( - + { - let cache: ExceptionsCache; - const body = Buffer.from('body'); - - beforeEach(() => { - jest.clearAllMocks(); - cache = new ExceptionsCache(3); - }); - - test('it should cache', async () => { - cache.set('test', body); - const cacheResp = cache.get('test'); - expect(cacheResp).toEqual(body); - }); - - test('it should handle cache miss', async () => { - cache.set('test', body); - const cacheResp = cache.get('not test'); - expect(cacheResp).toEqual(undefined); - }); - - test('it should handle cache eviction', async () => { - const a = Buffer.from('a'); - const b = Buffer.from('b'); - const c = Buffer.from('c'); - const d = Buffer.from('d'); - cache.set('1', a); - cache.set('2', b); - cache.set('3', c); - const cacheResp = cache.get('1'); - expect(cacheResp).toEqual(a); - - cache.set('4', d); - const secondResp = cache.get('1'); - expect(secondResp).toEqual(undefined); - expect(cache.get('2')).toEqual(b); - expect(cache.get('3')).toEqual(c); - expect(cache.get('4')).toEqual(d); - }); -}); diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/cache.ts b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/cache.ts deleted file mode 100644 index b9d3bae4e6ef9..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/cache.ts +++ /dev/null @@ -1,37 +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. - */ - -const DEFAULT_MAX_SIZE = 10; - -/** - * FIFO cache implementation for artifact downloads. - */ -export class ExceptionsCache { - private cache: Map; - private queue: string[]; - private maxSize: number; - - constructor(maxSize: number) { - this.cache = new Map(); - this.queue = []; - this.maxSize = maxSize || DEFAULT_MAX_SIZE; - } - - set(id: string, body: Buffer) { - if (this.queue.length + 1 > this.maxSize) { - const entry = this.queue.shift(); - if (entry !== undefined) { - this.cache.delete(entry); - } - } - this.queue.push(id); - this.cache.set(id, body); - } - - get(id: string): Buffer | undefined { - return this.cache.get(id); - } -} diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/index.ts b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/index.ts index ee7d44459aa38..21a9047a04299 100644 --- a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/index.ts +++ b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/index.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export * from './cache'; export * from './common'; export * from './lists'; export * from './manifest'; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/artifacts/download_exception_list.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/artifacts/download_exception_list.test.ts index 8c6faee7f7a5d..4454da855569b 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/artifacts/download_exception_list.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/artifacts/download_exception_list.test.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { deflateSync, inflateSync } from 'zlib'; +import LRU from 'lru-cache'; import { ILegacyClusterClient, IRouter, @@ -22,7 +23,6 @@ import { httpServerMock, loggingSystemMock, } from 'src/core/server/mocks'; -import { ExceptionsCache } from '../../lib/artifacts/cache'; import { ArtifactConstants } from '../../lib/artifacts'; import { registerDownloadExceptionListRoute } from './download_exception_list'; import { EndpointAppContextService } from '../../endpoint_app_context_services'; @@ -97,7 +97,7 @@ describe('test alerts route', () => { let routeConfig: RouteConfig; let routeHandler: RequestHandler; let endpointAppContextService: EndpointAppContextService; - let cache: ExceptionsCache; + let cache: LRU; let ingestSavedObjectClient: jest.Mocked; beforeEach(() => { @@ -108,7 +108,7 @@ describe('test alerts route', () => { mockClusterClient.asScoped.mockReturnValue(mockScopedClient); routerMock = httpServiceMock.createRouter(); endpointAppContextService = new EndpointAppContextService(); - cache = new ExceptionsCache(5); + cache = new LRU({ max: 10, maxAge: 1000 * 60 * 60 }); const startContract = createMockEndpointAppContextServiceStartContract(); // The authentication with the Fleet Plugin needs a separate scoped SO Client @@ -164,7 +164,7 @@ describe('test alerts route', () => { path.startsWith('/api/endpoint/artifacts/download') )!; - expect(routeConfig.options).toEqual(undefined); + expect(routeConfig.options).toEqual({ tags: ['endpoint:limited-concurrency'] }); await routeHandler( ({ diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/artifacts/download_exception_list.ts b/x-pack/plugins/security_solution/server/endpoint/routes/artifacts/download_exception_list.ts index 218f7c059da48..38e900c4d5015 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/artifacts/download_exception_list.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/artifacts/download_exception_list.ts @@ -11,11 +11,13 @@ import { IKibanaResponse, SavedObject, } from 'src/core/server'; +import LRU from 'lru-cache'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { authenticateAgentWithAccessToken } from '../../../../../ingest_manager/server/services/agents/authenticate'; import { validate } from '../../../../common/validate'; +import { LIMITED_CONCURRENCY_ENDPOINT_ROUTE_TAG } from '../../../../common/endpoint/constants'; import { buildRouteValidation } from '../../../utils/build_validation/route_validation'; -import { ArtifactConstants, ExceptionsCache } from '../../lib/artifacts'; +import { ArtifactConstants } from '../../lib/artifacts'; import { DownloadArtifactRequestParamsSchema, downloadArtifactRequestParamsSchema, @@ -32,7 +34,7 @@ const allowlistBaseRoute: string = '/api/endpoint/artifacts'; export function registerDownloadExceptionListRoute( router: IRouter, endpointContext: EndpointAppContext, - cache: ExceptionsCache + cache: LRU ) { router.get( { @@ -43,6 +45,7 @@ export function registerDownloadExceptionListRoute( DownloadArtifactRequestParamsSchema >(downloadArtifactRequestParamsSchema), }, + options: { tags: [LIMITED_CONCURRENCY_ENDPOINT_ROUTE_TAG] }, }, async (context, req, res) => { let scopedSOClient: SavedObjectsClientContract; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/limited_concurrency.ts b/x-pack/plugins/security_solution/server/endpoint/routes/limited_concurrency.ts new file mode 100644 index 0000000000000..767ba31311a77 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/routes/limited_concurrency.ts @@ -0,0 +1,72 @@ +/* + * 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 { + CoreSetup, + KibanaRequest, + LifecycleResponseFactory, + OnPreAuthToolkit, +} from 'kibana/server'; +import { + LIMITED_CONCURRENCY_ENDPOINT_ROUTE_TAG, + LIMITED_CONCURRENCY_ENDPOINT_COUNT, +} from '../../../common/endpoint/constants'; + +class MaxCounter { + constructor(private readonly max: number = 1) {} + private counter = 0; + valueOf() { + return this.counter; + } + increase() { + if (this.counter < this.max) { + this.counter += 1; + } + } + decrease() { + if (this.counter > 0) { + this.counter -= 1; + } + } + lessThanMax() { + return this.counter < this.max; + } +} + +function shouldHandleRequest(request: KibanaRequest) { + const tags = request.route.options.tags; + return tags.includes(LIMITED_CONCURRENCY_ENDPOINT_ROUTE_TAG); +} + +export function registerLimitedConcurrencyRoutes(core: CoreSetup) { + const counter = new MaxCounter(LIMITED_CONCURRENCY_ENDPOINT_COUNT); + core.http.registerOnPreAuth(function preAuthHandler( + request: KibanaRequest, + response: LifecycleResponseFactory, + toolkit: OnPreAuthToolkit + ) { + if (!shouldHandleRequest(request)) { + return toolkit.next(); + } + + if (!counter.lessThanMax()) { + return response.customError({ + body: 'Too Many Requests', + statusCode: 429, + }); + } + + counter.increase(); + + // requests.events.aborted$ has a bug (but has test which explicitly verifies) where it's fired even when the request completes + // https://github.com/elastic/kibana/pull/70495#issuecomment-656288766 + request.events.aborted$.toPromise().then(() => { + counter.decrease(); + }); + + return toolkit.next(); + }); +} diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.mock.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.mock.ts index 2ebffa6fb3ad8..592ffb0eae62a 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.mock.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.mock.ts @@ -9,7 +9,7 @@ import { Logger } from 'src/core/server'; import { PackageConfigServiceInterface } from '../../../../../../ingest_manager/server'; import { createPackageConfigServiceMock } from '../../../../../../ingest_manager/server/mocks'; import { listMock } from '../../../../../../lists/server/mocks'; -import { ExceptionsCache } from '../../../lib/artifacts'; +import LRU from 'lru-cache'; import { getArtifactClientMock } from '../artifact_client.mock'; import { getManifestClientMock } from '../manifest_client.mock'; import { ManifestManager } from './manifest_manager'; @@ -28,11 +28,11 @@ export enum ManifestManagerMockType { export const getManifestManagerMock = (opts?: { mockType?: ManifestManagerMockType; - cache?: ExceptionsCache; + cache?: LRU; packageConfigService?: jest.Mocked; savedObjectsClient?: ReturnType; }): ManifestManager => { - let cache = new ExceptionsCache(5); + let cache = new LRU({ max: 10, maxAge: 1000 * 60 * 60 }); if (opts?.cache !== undefined) { cache = opts.cache; } diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts index ff331f7d017f4..c838f772fb66b 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts @@ -7,13 +7,10 @@ import { inflateSync } from 'zlib'; import { savedObjectsClientMock } from 'src/core/server/mocks'; import { createPackageConfigServiceMock } from '../../../../../../ingest_manager/server/mocks'; -import { - ArtifactConstants, - ManifestConstants, - ExceptionsCache, - isCompleteArtifact, -} from '../../../lib/artifacts'; +import { ArtifactConstants, ManifestConstants, isCompleteArtifact } from '../../../lib/artifacts'; + import { getManifestManagerMock } from './manifest_manager.mock'; +import LRU from 'lru-cache'; describe('manifest_manager', () => { describe('ManifestManager sanity checks', () => { @@ -41,7 +38,7 @@ describe('manifest_manager', () => { }); test('ManifestManager populates cache properly', async () => { - const cache = new ExceptionsCache(5); + const cache = new LRU({ max: 10, maxAge: 1000 * 60 * 60 }); const manifestManager = getManifestManagerMock({ cache }); const oldManifest = await manifestManager.getLastComputedManifest( ManifestConstants.SCHEMA_VERSION diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts index 2501f07cb26e0..13ca51e1f2b39 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts @@ -5,6 +5,7 @@ */ import { Logger, SavedObjectsClientContract } from 'src/core/server'; +import LRU from 'lru-cache'; import { PackageConfigServiceInterface } from '../../../../../../ingest_manager/server'; import { ExceptionListClient } from '../../../../../../lists/server'; import { ManifestSchemaVersion } from '../../../../../common/endpoint/schema/common'; @@ -16,7 +17,6 @@ import { Manifest, buildArtifact, getFullEndpointExceptionList, - ExceptionsCache, ManifestDiff, getArtifactId, } from '../../../lib/artifacts'; @@ -33,7 +33,7 @@ export interface ManifestManagerContext { exceptionListClient: ExceptionListClient; packageConfigService: PackageConfigServiceInterface; logger: Logger; - cache: ExceptionsCache; + cache: LRU; } export interface ManifestSnapshotOpts { @@ -51,7 +51,7 @@ export class ManifestManager { protected packageConfigService: PackageConfigServiceInterface; protected savedObjectsClient: SavedObjectsClientContract; protected logger: Logger; - protected cache: ExceptionsCache; + protected cache: LRU; constructor(context: ManifestManagerContext) { this.artifactClient = context.artifactClient; diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 22b55c64a1657..611f35c6d402a 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -7,6 +7,7 @@ import { Observable } from 'rxjs'; import { first } from 'rxjs/operators'; import { i18n } from '@kbn/i18n'; +import LRU from 'lru-cache'; import { CoreSetup, @@ -34,13 +35,14 @@ import { isAlertExecutor } from './lib/detection_engine/signals/types'; import { signalRulesAlertType } from './lib/detection_engine/signals/signal_rule_alert_type'; import { rulesNotificationAlertType } from './lib/detection_engine/notifications/rules_notification_alert_type'; import { isNotificationAlertExecutor } from './lib/detection_engine/notifications/types'; -import { ManifestTask, ExceptionsCache } from './endpoint/lib/artifacts'; +import { ManifestTask } from './endpoint/lib/artifacts'; import { initSavedObjects, savedObjectTypes } from './saved_objects'; import { AppClientFactory } from './client'; import { createConfig$, ConfigType } from './config'; import { initUiSettings } from './ui_settings'; import { APP_ID, APP_ICON, SERVER_APP_ID, SecurityPageName } from '../common/constants'; import { registerEndpointRoutes } from './endpoint/routes/metadata'; +import { registerLimitedConcurrencyRoutes } from './endpoint/routes/limited_concurrency'; import { registerResolverRoutes } from './endpoint/routes/resolver'; import { registerPolicyRoutes } from './endpoint/routes/policy'; import { ArtifactClient, ManifestManager } from './endpoint/services'; @@ -94,14 +96,15 @@ export class Plugin implements IPlugin; constructor(context: PluginInitializerContext) { this.context = context; this.logger = context.logger.get('plugins', APP_ID); this.config$ = createConfig$(context); this.appClientFactory = new AppClientFactory(); - this.exceptionsCache = new ExceptionsCache(5); // TODO + // Cache up to three artifacts with a max retention of 5 mins each + this.exceptionsCache = new LRU({ max: 3, maxAge: 1000 * 60 * 5 }); this.logger.debug('plugin initialized'); } @@ -149,6 +152,7 @@ export class Plugin implements IPlugin { +describe.skip('Bulk Operation Buffer', () => { describe('createBuffer()', () => { test('batches up multiple Operation calls', async () => { const bulkUpdate: jest.Mocked> = jest.fn(