From 0a6da70a89b519595317eddfff05a75518f86d0e Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Wed, 22 Apr 2020 19:21:14 +0300 Subject: [PATCH 01/39] [Regression] Histogram aggregation always shows an error message (#63484) * WIP [Regression] Histogram aggregation always shows an error message Closes: #62624 * make getInternalStartServices private * fix ts issues * remove createSearchSource from static contract * fix some jest test * move searh_source to static contract * fix types * fix function tests * fix jest / add createStartServicesGetter * fix comments: saved_object_management * maps: fix PR comments * maps: update types * fix heck_published_api_changes * move searchSource into runtime contract * cleanup * fix ts error * cleanup * remove extra dependencies * fix Discover * fix Discover JEST * fix PR comments Co-authored-by: Elastic Machine --- ...-plugins-data-public.createsearchsource.md | 15 -- ...lugin-plugins-data-public.isearchsource.md | 2 + .../kibana-plugin-plugins-data-public.md | 4 +- ...ibana-plugin-plugins-data-public.search.md | 3 +- ...-data-public.searchsource._constructor_.md | 20 -- ...plugins-data-public.searchsource.create.md | 15 -- ...ns-data-public.searchsource.createchild.md | 22 -- ...ins-data-public.searchsource.createcopy.md | 15 -- ...lugins-data-public.searchsource.destroy.md | 17 -- ...-plugins-data-public.searchsource.fetch.md | 25 --- ...ugins-data-public.searchsource.getfield.md | 25 --- ...gins-data-public.searchsource.getfields.md | 49 ----- ...-plugins-data-public.searchsource.getid.md | 15 -- ...ns-data-public.searchsource.getownfield.md | 24 --- ...gins-data-public.searchsource.getparent.md | 17 -- ...ublic.searchsource.getsearchrequestbody.md | 15 -- ...lugins-data-public.searchsource.history.md | 11 - ...plugin-plugins-data-public.searchsource.md | 46 ----- ...data-public.searchsource.onrequeststart.md | 24 --- ...gins-data-public.searchsource.serialize.md | 27 --- ...ugins-data-public.searchsource.setfield.md | 23 --- ...gins-data-public.searchsource.setfields.md | 22 -- ...gins-data-public.searchsource.setparent.md | 25 --- ...archsource.setpreferredsearchstrategyid.md | 24 --- .../__snapshots__/controls_tab.test.tsx.snap | 10 + .../public/control/control.test.ts | 18 +- .../public/control/control.ts | 4 +- .../public/control/create_search_source.ts | 14 +- .../control/list_control_factory.test.ts | 193 +++++++++--------- .../public/control/list_control_factory.ts | 20 +- .../control/range_control_factory.test.ts | 124 +++++------ .../public/control/range_control_factory.ts | 19 +- .../public/input_control_fn.test.ts | 5 +- .../public/legacy_imports.ts | 26 --- .../public/test_utils/get_deps_mock.tsx | 15 +- .../test_utils/get_search_service_mock.ts | 4 +- .../public/vis_controller.tsx | 5 +- .../kibana/public/discover/build_services.ts | 4 +- .../kibana/public/discover/kibana_services.ts | 1 - .../np_ready/angular/context/api/_stubs.js | 26 +-- .../angular/context/api/anchor.test.js | 4 - .../context/api/context.predecessors.test.js | 70 ++++--- .../context/api/context.successors.test.js | 73 ++++--- .../np_ready/angular/context/api/context.ts | 7 +- .../np_ready/angular/context/query/actions.js | 6 +- .../test_helpers/get_saved_dashboard_mock.ts | 6 +- src/plugins/data/public/index.ts | 2 - src/plugins/data/public/mocks.ts | 2 +- src/plugins/data/public/plugin.ts | 30 ++- src/plugins/data/public/public.api.md | 108 +++------- .../public/search/aggs/agg_params.test.ts | 10 +- .../data/public/search/aggs/agg_type.test.ts | 16 +- .../create_filter/date_histogram.test.ts | 10 +- .../buckets/create_filter/date_range.test.ts | 10 +- .../buckets/create_filter/filters.test.ts | 10 +- .../buckets/create_filter/ip_range.test.ts | 10 +- .../aggs/buckets/create_filter/range.test.ts | 10 +- .../aggs/buckets/create_filter/terms.test.ts | 10 +- .../search/aggs/buckets/date_range.test.ts | 10 +- .../search/aggs/buckets/geo_hash.test.ts | 10 +- .../search/aggs/buckets/histogram.test.ts | 10 +- .../public/search/aggs/buckets/range.test.ts | 10 +- .../aggs/buckets/significant_terms.test.ts | 10 +- .../data/public/search/aggs/index.test.ts | 10 +- .../public/search/aggs/metrics/median.test.ts | 10 +- .../aggs/metrics/parent_pipeline.test.ts | 11 +- .../aggs/metrics/percentile_ranks.test.ts | 10 +- .../search/aggs/metrics/percentiles.test.ts | 10 +- .../aggs/metrics/sibling_pipeline.test.ts | 11 +- .../search/aggs/metrics/std_deviation.test.ts | 10 +- .../search/aggs/metrics/top_hit.test.ts | 10 +- .../search/aggs/param_types/field.test.ts | 10 +- .../test_helpers/mock_agg_types_registry.ts | 19 +- .../data/public/search/expressions/esaggs.ts | 5 +- src/plugins/data/public/search/fetch/types.ts | 4 +- src/plugins/data/public/search/index.ts | 2 +- .../public/search/legacy/call_client.test.ts | 2 +- .../legacy/default_search_strategy.test.ts | 4 +- .../search/legacy/default_search_strategy.ts | 4 +- src/plugins/data/public/search/mocks.ts | 11 +- .../data/public/search/search_service.ts | 70 ++++--- .../create_search_source.test.ts | 35 +++- .../search_source/create_search_source.ts | 20 +- .../data/public/search/search_source/index.ts | 4 +- .../data/public/search/search_source/mocks.ts | 28 ++- .../search_source/search_source.test.ts | 161 ++++++++------- .../search/search_source/search_source.ts | 86 +++++--- src/plugins/data/public/search/types.ts | 14 +- src/plugins/data/public/services.ts | 4 +- src/plugins/data/public/types.ts | 7 +- .../saved_object/helpers/apply_es_resp.ts | 11 +- .../helpers/build_saved_object.ts | 12 +- .../public/saved_object/saved_object.test.ts | 111 +++++----- .../public/lib/resolve_saved_objects.test.ts | 38 +++- .../public/lib/resolve_saved_objects.ts | 9 +- .../management_section/mount_section.tsx | 1 + .../saved_objects_table.test.tsx.snap | 35 ++++ .../objects_table/components/flyout.test.tsx | 8 +- .../objects_table/components/flyout.tsx | 14 +- .../saved_objects_table.test.tsx | 3 + .../objects_table/saved_objects_table.tsx | 3 + .../saved_objects_management/public/plugin.ts | 3 +- .../public/saved_visualizations/_saved_vis.ts | 10 +- .../plugins/maps/public/kibana_services.d.ts | 5 +- x-pack/plugins/maps/public/kibana_services.js | 6 +- .../es_search_source/es_search_source.js | 8 +- .../layers/sources/es_source/es_source.d.ts | 6 +- .../layers/sources/es_source/es_source.js | 12 +- x-pack/plugins/maps/public/plugin.ts | 3 +- 109 files changed, 1001 insertions(+), 1286 deletions(-) delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.createsearchsource.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource._constructor_.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.create.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.createchild.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.createcopy.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.destroy.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.fetch.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getfield.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getfields.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getid.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getownfield.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getparent.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getsearchrequestbody.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.history.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.onrequeststart.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.serialize.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setfield.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setfields.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setparent.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setpreferredsearchstrategyid.md delete mode 100644 src/legacy/core_plugins/input_control_vis/public/legacy_imports.ts diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.createsearchsource.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.createsearchsource.md deleted file mode 100644 index 5c5aa348eecdf..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.createsearchsource.md +++ /dev/null @@ -1,15 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [createSearchSource](./kibana-plugin-plugins-data-public.createsearchsource.md) - -## createSearchSource variable - -Deserializes a json string and a set of referenced objects to a `SearchSource` instance. Use this method to re-create the search source serialized using `searchSource.serialize`. - -This function is a factory function that returns the actual utility when calling it with the required service dependency (index patterns contract). A pre-wired version is also exposed in the start contract of the data plugin as part of the search service - -Signature: - -```typescript -createSearchSource: (indexPatterns: Pick) => (searchSourceJson: string, references: SavedObjectReference[]) => Promise -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsource.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsource.md index a8154dff72a6a..4b9f6e3594dc5 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsource.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsource.md @@ -4,6 +4,8 @@ ## ISearchSource type +\* + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md index bf29c883e4eb9..5ef317a57fb0e 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md @@ -21,7 +21,6 @@ | [RequestTimeoutError](./kibana-plugin-plugins-data-public.requesttimeouterror.md) | Class used to signify that a request timed out. Useful for applications to conditionally handle this type of error differently than other errors. | | [SearchError](./kibana-plugin-plugins-data-public.searcherror.md) | | | [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) | | -| [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) | | | [TimeHistory](./kibana-plugin-plugins-data-public.timehistory.md) | | ## Enumerations @@ -101,7 +100,6 @@ | [castEsToKbnFieldTypeName](./kibana-plugin-plugins-data-public.castestokbnfieldtypename.md) | Get the KbnFieldType name for an esType string | | [connectToQueryState](./kibana-plugin-plugins-data-public.connecttoquerystate.md) | Helper to setup two-way syncing of global data and a state container | | [createSavedQueryService](./kibana-plugin-plugins-data-public.createsavedqueryservice.md) | | -| [createSearchSource](./kibana-plugin-plugins-data-public.createsearchsource.md) | Deserializes a json string and a set of referenced objects to a SearchSource instance. Use this method to re-create the search source serialized using searchSource.serialize.This function is a factory function that returns the actual utility when calling it with the required service dependency (index patterns contract). A pre-wired version is also exposed in the start contract of the data plugin as part of the search service | | [ES\_SEARCH\_STRATEGY](./kibana-plugin-plugins-data-public.es_search_strategy.md) | | | [esFilters](./kibana-plugin-plugins-data-public.esfilters.md) | | | [esKuery](./kibana-plugin-plugins-data-public.eskuery.md) | | @@ -140,7 +138,7 @@ | [IpRangeKey](./kibana-plugin-plugins-data-public.iprangekey.md) | | | [ISearch](./kibana-plugin-plugins-data-public.isearch.md) | | | [ISearchGeneric](./kibana-plugin-plugins-data-public.isearchgeneric.md) | | -| [ISearchSource](./kibana-plugin-plugins-data-public.isearchsource.md) | | +| [ISearchSource](./kibana-plugin-plugins-data-public.isearchsource.md) | \* | | [MatchAllFilter](./kibana-plugin-plugins-data-public.matchallfilter.md) | | | [ParsedInterval](./kibana-plugin-plugins-data-public.parsedinterval.md) | | | [PhraseFilter](./kibana-plugin-plugins-data-public.phrasefilter.md) | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md index 78ac05b9fd386..9a22339fd0530 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md @@ -27,8 +27,9 @@ search: { InvalidEsCalendarIntervalError: typeof InvalidEsCalendarIntervalError; InvalidEsIntervalFormatError: typeof InvalidEsIntervalFormatError; isDateHistogramBucketAggConfig: typeof isDateHistogramBucketAggConfig; + isNumberType: (agg: import("./search").AggConfig) => boolean; isStringType: (agg: import("./search").AggConfig) => boolean; - isType: (type: string) => (agg: import("./search").AggConfig) => boolean; + isType: (...types: string[]) => (agg: import("./search").AggConfig) => boolean; isValidEsInterval: typeof isValidEsInterval; isValidInterval: typeof isValidInterval; parentPipelineType: string; diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource._constructor_.md deleted file mode 100644 index e0c9e77b313a5..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource._constructor_.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [(constructor)](./kibana-plugin-plugins-data-public.searchsource._constructor_.md) - -## SearchSource.(constructor) - -Constructs a new instance of the `SearchSource` class - -Signature: - -```typescript -constructor(fields?: SearchSourceFields); -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| fields | SearchSourceFields | | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.create.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.create.md deleted file mode 100644 index b0a0201680ca8..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.create.md +++ /dev/null @@ -1,15 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [create](./kibana-plugin-plugins-data-public.searchsource.create.md) - -## SearchSource.create() method - -Signature: - -```typescript -create(): SearchSource; -``` -Returns: - -`SearchSource` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.createchild.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.createchild.md deleted file mode 100644 index 3f17dc21cf514..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.createchild.md +++ /dev/null @@ -1,22 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [createChild](./kibana-plugin-plugins-data-public.searchsource.createchild.md) - -## SearchSource.createChild() method - -Signature: - -```typescript -createChild(options?: {}): SearchSource; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| options | {} | | - -Returns: - -`SearchSource` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.createcopy.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.createcopy.md deleted file mode 100644 index f503a3dfc3299..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.createcopy.md +++ /dev/null @@ -1,15 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [createCopy](./kibana-plugin-plugins-data-public.searchsource.createcopy.md) - -## SearchSource.createCopy() method - -Signature: - -```typescript -createCopy(): SearchSource; -``` -Returns: - -`SearchSource` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.destroy.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.destroy.md deleted file mode 100644 index 8a7cc5ee75d11..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.destroy.md +++ /dev/null @@ -1,17 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [destroy](./kibana-plugin-plugins-data-public.searchsource.destroy.md) - -## SearchSource.destroy() method - -Completely destroy the SearchSource. {undefined} - -Signature: - -```typescript -destroy(): void; -``` -Returns: - -`void` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.fetch.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.fetch.md deleted file mode 100644 index 208ce565fac13..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.fetch.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [fetch](./kibana-plugin-plugins-data-public.searchsource.fetch.md) - -## SearchSource.fetch() method - -Fetch this source and reject the returned Promise on error - - -Signature: - -```typescript -fetch(options?: FetchOptions): Promise; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| options | FetchOptions | | - -Returns: - -`Promise` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getfield.md deleted file mode 100644 index 98ba815696cf6..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getfield.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [getField](./kibana-plugin-plugins-data-public.searchsource.getfield.md) - -## SearchSource.getField() method - -Get fields from the fields - -Signature: - -```typescript -getField(field: K, recurse?: boolean): SearchSourceFields[K]; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| field | K | | -| recurse | boolean | | - -Returns: - -`SearchSourceFields[K]` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getfields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getfields.md deleted file mode 100644 index dce03e7e1a95c..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getfields.md +++ /dev/null @@ -1,49 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [getFields](./kibana-plugin-plugins-data-public.searchsource.getfields.md) - -## SearchSource.getFields() method - -Signature: - -```typescript -getFields(): { - type?: string | undefined; - query?: import("../..").Query | undefined; - filter?: Filter | Filter[] | (() => Filter | Filter[] | undefined) | undefined; - sort?: Record | Record[] | undefined; - highlight?: any; - highlightAll?: boolean | undefined; - aggs?: any; - from?: number | undefined; - size?: number | undefined; - source?: string | boolean | string[] | undefined; - version?: boolean | undefined; - fields?: string | boolean | string[] | undefined; - index?: import("../..").IndexPattern | undefined; - searchAfter?: import("./types").EsQuerySearchAfter | undefined; - timeout?: string | undefined; - terminate_after?: number | undefined; - }; -``` -Returns: - -`{ - type?: string | undefined; - query?: import("../..").Query | undefined; - filter?: Filter | Filter[] | (() => Filter | Filter[] | undefined) | undefined; - sort?: Record | Record[] | undefined; - highlight?: any; - highlightAll?: boolean | undefined; - aggs?: any; - from?: number | undefined; - size?: number | undefined; - source?: string | boolean | string[] | undefined; - version?: boolean | undefined; - fields?: string | boolean | string[] | undefined; - index?: import("../..").IndexPattern | undefined; - searchAfter?: import("./types").EsQuerySearchAfter | undefined; - timeout?: string | undefined; - terminate_after?: number | undefined; - }` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getid.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getid.md deleted file mode 100644 index 55aaa26ca62f3..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getid.md +++ /dev/null @@ -1,15 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [getId](./kibana-plugin-plugins-data-public.searchsource.getid.md) - -## SearchSource.getId() method - -Signature: - -```typescript -getId(): string; -``` -Returns: - -`string` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getownfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getownfield.md deleted file mode 100644 index d5a133772264e..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getownfield.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [getOwnField](./kibana-plugin-plugins-data-public.searchsource.getownfield.md) - -## SearchSource.getOwnField() method - -Get the field from our own fields, don't traverse up the chain - -Signature: - -```typescript -getOwnField(field: K): SearchSourceFields[K]; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| field | K | | - -Returns: - -`SearchSourceFields[K]` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getparent.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getparent.md deleted file mode 100644 index 14578f7949ba6..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getparent.md +++ /dev/null @@ -1,17 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [getParent](./kibana-plugin-plugins-data-public.searchsource.getparent.md) - -## SearchSource.getParent() method - -Get the parent of this SearchSource {undefined\|searchSource} - -Signature: - -```typescript -getParent(): SearchSource | undefined; -``` -Returns: - -`SearchSource | undefined` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getsearchrequestbody.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getsearchrequestbody.md deleted file mode 100644 index f3451c9391074..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getsearchrequestbody.md +++ /dev/null @@ -1,15 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [getSearchRequestBody](./kibana-plugin-plugins-data-public.searchsource.getsearchrequestbody.md) - -## SearchSource.getSearchRequestBody() method - -Signature: - -```typescript -getSearchRequestBody(): Promise; -``` -Returns: - -`Promise` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.history.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.history.md deleted file mode 100644 index e77c9dac7239f..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.history.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [history](./kibana-plugin-plugins-data-public.searchsource.history.md) - -## SearchSource.history property - -Signature: - -```typescript -history: SearchRequest[]; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.md deleted file mode 100644 index 5f2fc809a5590..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.md +++ /dev/null @@ -1,46 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) - -## SearchSource class - -Signature: - -```typescript -export declare class SearchSource -``` - -## Constructors - -| Constructor | Modifiers | Description | -| --- | --- | --- | -| [(constructor)(fields)](./kibana-plugin-plugins-data-public.searchsource._constructor_.md) | | Constructs a new instance of the SearchSource class | - -## Properties - -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [history](./kibana-plugin-plugins-data-public.searchsource.history.md) | | SearchRequest[] | | - -## Methods - -| Method | Modifiers | Description | -| --- | --- | --- | -| [create()](./kibana-plugin-plugins-data-public.searchsource.create.md) | | | -| [createChild(options)](./kibana-plugin-plugins-data-public.searchsource.createchild.md) | | | -| [createCopy()](./kibana-plugin-plugins-data-public.searchsource.createcopy.md) | | | -| [destroy()](./kibana-plugin-plugins-data-public.searchsource.destroy.md) | | Completely destroy the SearchSource. {undefined} | -| [fetch(options)](./kibana-plugin-plugins-data-public.searchsource.fetch.md) | | Fetch this source and reject the returned Promise on error | -| [getField(field, recurse)](./kibana-plugin-plugins-data-public.searchsource.getfield.md) | | Get fields from the fields | -| [getFields()](./kibana-plugin-plugins-data-public.searchsource.getfields.md) | | | -| [getId()](./kibana-plugin-plugins-data-public.searchsource.getid.md) | | | -| [getOwnField(field)](./kibana-plugin-plugins-data-public.searchsource.getownfield.md) | | Get the field from our own fields, don't traverse up the chain | -| [getParent()](./kibana-plugin-plugins-data-public.searchsource.getparent.md) | | Get the parent of this SearchSource {undefined\|searchSource} | -| [getSearchRequestBody()](./kibana-plugin-plugins-data-public.searchsource.getsearchrequestbody.md) | | | -| [onRequestStart(handler)](./kibana-plugin-plugins-data-public.searchsource.onrequeststart.md) | | Add a handler that will be notified whenever requests start | -| [serialize()](./kibana-plugin-plugins-data-public.searchsource.serialize.md) | | Serializes the instance to a JSON string and a set of referenced objects. Use this method to get a representation of the search source which can be stored in a saved object.The references returned by this function can be mixed with other references in the same object, however make sure there are no name-collisions. The references will be named kibanaSavedObjectMeta.searchSourceJSON.index and kibanaSavedObjectMeta.searchSourceJSON.filter[<number>].meta.index.Using createSearchSource, the instance can be re-created. | -| [setField(field, value)](./kibana-plugin-plugins-data-public.searchsource.setfield.md) | | | -| [setFields(newFields)](./kibana-plugin-plugins-data-public.searchsource.setfields.md) | | | -| [setParent(parent, options)](./kibana-plugin-plugins-data-public.searchsource.setparent.md) | | Set a searchSource that this source should inherit from | -| [setPreferredSearchStrategyId(searchStrategyId)](./kibana-plugin-plugins-data-public.searchsource.setpreferredsearchstrategyid.md) | | \*\*\* PUBLIC API \*\*\* | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.onrequeststart.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.onrequeststart.md deleted file mode 100644 index 092d057c69196..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.onrequeststart.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [onRequestStart](./kibana-plugin-plugins-data-public.searchsource.onrequeststart.md) - -## SearchSource.onRequestStart() method - -Add a handler that will be notified whenever requests start - -Signature: - -```typescript -onRequestStart(handler: (searchSource: ISearchSource, options?: FetchOptions) => Promise): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| handler | (searchSource: ISearchSource, options?: FetchOptions) => Promise<unknown> | | - -Returns: - -`void` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.serialize.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.serialize.md deleted file mode 100644 index 52d25dec01dfd..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.serialize.md +++ /dev/null @@ -1,27 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [serialize](./kibana-plugin-plugins-data-public.searchsource.serialize.md) - -## SearchSource.serialize() method - -Serializes the instance to a JSON string and a set of referenced objects. Use this method to get a representation of the search source which can be stored in a saved object. - -The references returned by this function can be mixed with other references in the same object, however make sure there are no name-collisions. The references will be named `kibanaSavedObjectMeta.searchSourceJSON.index` and `kibanaSavedObjectMeta.searchSourceJSON.filter[].meta.index`. - -Using `createSearchSource`, the instance can be re-created. - -Signature: - -```typescript -serialize(): { - searchSourceJSON: string; - references: SavedObjectReference[]; - }; -``` -Returns: - -`{ - searchSourceJSON: string; - references: SavedObjectReference[]; - }` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setfield.md deleted file mode 100644 index 83b7c30281752..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setfield.md +++ /dev/null @@ -1,23 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [setField](./kibana-plugin-plugins-data-public.searchsource.setfield.md) - -## SearchSource.setField() method - -Signature: - -```typescript -setField(field: K, value: SearchSourceFields[K]): this; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| field | K | | -| value | SearchSourceFields[K] | | - -Returns: - -`this` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setfields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setfields.md deleted file mode 100644 index fa9b265aa43b7..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setfields.md +++ /dev/null @@ -1,22 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [setFields](./kibana-plugin-plugins-data-public.searchsource.setfields.md) - -## SearchSource.setFields() method - -Signature: - -```typescript -setFields(newFields: SearchSourceFields): this; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| newFields | SearchSourceFields | | - -Returns: - -`this` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setparent.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setparent.md deleted file mode 100644 index 19bf10bec210f..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setparent.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [setParent](./kibana-plugin-plugins-data-public.searchsource.setparent.md) - -## SearchSource.setParent() method - -Set a searchSource that this source should inherit from - -Signature: - -```typescript -setParent(parent?: ISearchSource, options?: SearchSourceOptions): this; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| parent | ISearchSource | | -| options | SearchSourceOptions | | - -Returns: - -`this` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setpreferredsearchstrategyid.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setpreferredsearchstrategyid.md deleted file mode 100644 index 8d8dbce9e60f6..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setpreferredsearchstrategyid.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [setPreferredSearchStrategyId](./kibana-plugin-plugins-data-public.searchsource.setpreferredsearchstrategyid.md) - -## SearchSource.setPreferredSearchStrategyId() method - -\*\*\* PUBLIC API \*\*\* - -Signature: - -```typescript -setPreferredSearchStrategyId(searchStrategyId: string): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| searchStrategyId | string | | - -Returns: - -`void` - diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/controls_tab.test.tsx.snap b/src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/controls_tab.test.tsx.snap index 249f42a6ebf3f..72cb399777b77 100644 --- a/src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/controls_tab.test.tsx.snap +++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/controls_tab.test.tsx.snap @@ -40,6 +40,11 @@ exports[`renders ControlsTab 1`] = ` "timefilter": Object {}, }, }, + "search": Object { + "searchSource": Object { + "create": [MockFunction], + }, + }, }, } } @@ -96,6 +101,11 @@ exports[`renders ControlsTab 1`] = ` "timefilter": Object {}, }, }, + "search": Object { + "searchSource": Object { + "create": [MockFunction], + }, + }, }, } } diff --git a/src/legacy/core_plugins/input_control_vis/public/control/control.test.ts b/src/legacy/core_plugins/input_control_vis/public/control/control.test.ts index e76b199a0262c..a2d220c14a3f7 100644 --- a/src/legacy/core_plugins/input_control_vis/public/control/control.test.ts +++ b/src/legacy/core_plugins/input_control_vis/public/control/control.test.ts @@ -21,7 +21,6 @@ import expect from '@kbn/expect'; import { Control } from './control'; import { ControlParams } from '../editor_utils'; import { FilterManager as BaseFilterManager } from './filter_manager/filter_manager'; -import { SearchSource } from '../legacy_imports'; function createControlParams(id: string, label: string): ControlParams { return { @@ -51,18 +50,12 @@ class ControlMock extends Control { destroy() {} } -const mockKbnApi: SearchSource = {} as SearchSource; describe('hasChanged', () => { let control: ControlMock; beforeEach(() => { - control = new ControlMock( - createControlParams('3', 'control'), - mockFilterManager, - false, - mockKbnApi - ); + control = new ControlMock(createControlParams('3', 'control'), mockFilterManager, false); }); afterEach(() => { @@ -93,20 +86,17 @@ describe('ancestors', () => { grandParentControl = new ControlMock( createControlParams('1', 'grandparent control'), mockFilterManager, - false, - mockKbnApi + false ); parentControl = new ControlMock( createControlParams('2', 'parent control'), mockFilterManager, - false, - mockKbnApi + false ); childControl = new ControlMock( createControlParams('3', 'child control'), mockFilterManager, - false, - mockKbnApi + false ); }); diff --git a/src/legacy/core_plugins/input_control_vis/public/control/control.ts b/src/legacy/core_plugins/input_control_vis/public/control/control.ts index 6fddef777f73e..62e0090e466c0 100644 --- a/src/legacy/core_plugins/input_control_vis/public/control/control.ts +++ b/src/legacy/core_plugins/input_control_vis/public/control/control.ts @@ -23,7 +23,6 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; import { Filter } from '../../../../../plugins/data/public'; -import { SearchSource as SearchSourceClass } from '../legacy_imports'; import { ControlParams, ControlParamsOptions, CONTROL_TYPES } from '../editor_utils'; import { RangeFilterManager } from './filter_manager/range_filter_manager'; import { PhraseFilterManager } from './filter_manager/phrase_filter_manager'; @@ -61,8 +60,7 @@ export abstract class Control { constructor( public controlParams: ControlParams, public filterManager: FilterManager, - public useTimeFilter: boolean, - public SearchSource: SearchSourceClass + public useTimeFilter: boolean ) { this.id = controlParams.id; this.controlParams = controlParams; diff --git a/src/legacy/core_plugins/input_control_vis/public/control/create_search_source.ts b/src/legacy/core_plugins/input_control_vis/public/control/create_search_source.ts index f238a2287ecdb..8f86232f63be7 100644 --- a/src/legacy/core_plugins/input_control_vis/public/control/create_search_source.ts +++ b/src/legacy/core_plugins/input_control_vis/public/control/create_search_source.ts @@ -17,11 +17,16 @@ * under the License. */ -import { PhraseFilter, IndexPattern, TimefilterContract } from '../../../../../plugins/data/public'; -import { SearchSource as SearchSourceClass, SearchSourceFields } from '../legacy_imports'; +import { + SearchSourceFields, + PhraseFilter, + IndexPattern, + TimefilterContract, + DataPublicPluginStart, +} from '../../../../../plugins/data/public'; export function createSearchSource( - SearchSource: SearchSourceClass, + { create }: DataPublicPluginStart['search']['searchSource'], initialState: SearchSourceFields | null, indexPattern: IndexPattern, aggs: any, @@ -29,7 +34,8 @@ export function createSearchSource( filters: PhraseFilter[] = [], timefilter: TimefilterContract ) { - const searchSource = initialState ? new SearchSource(initialState) : new SearchSource(); + const searchSource = create(initialState || {}); + // Do not not inherit from rootSearchSource to avoid picking up time and globals searchSource.setParent(undefined); searchSource.setField('filter', () => { diff --git a/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.test.ts b/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.test.ts index e6426e5a4c69d..72070175a233c 100644 --- a/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.test.ts +++ b/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.test.ts @@ -21,98 +21,49 @@ import { listControlFactory, ListControl } from './list_control_factory'; import { ControlParams, CONTROL_TYPES } from '../editor_utils'; import { getDepsMock, getSearchSourceMock } from '../test_utils'; -const MockSearchSource = getSearchSourceMock(); -const deps = getDepsMock(); - -jest.doMock('./create_search_source.ts', () => ({ - createSearchSource: MockSearchSource, -})); - -describe('hasValue', () => { - const controlParams: ControlParams = { - id: '1', - fieldName: 'myField', - options: {} as any, - type: CONTROL_TYPES.LIST, - label: 'test', - indexPattern: {} as any, - parent: 'parent', - }; - const useTimeFilter = false; - - let listControl: ListControl; - beforeEach(async () => { - listControl = await listControlFactory(controlParams, useTimeFilter, MockSearchSource, deps); +describe('listControlFactory', () => { + const searchSourceMock = getSearchSourceMock(); + const deps = getDepsMock({ + searchSource: { + create: searchSourceMock, + }, }); - test('should be false when control has no value', () => { - expect(listControl.hasValue()).toBe(false); - }); + describe('hasValue', () => { + const controlParams: ControlParams = { + id: '1', + fieldName: 'myField', + options: {} as any, + type: CONTROL_TYPES.LIST, + label: 'test', + indexPattern: {} as any, + parent: 'parent', + }; + const useTimeFilter = false; - test('should be true when control has value', () => { - listControl.set([{ value: 'selected option', label: 'selection option' }]); - expect(listControl.hasValue()).toBe(true); - }); + let listControl: ListControl; + beforeEach(async () => { + listControl = await listControlFactory(controlParams, useTimeFilter, deps); + }); - test('should be true when control has value that is the string "false"', () => { - listControl.set([{ value: 'false', label: 'selection option' }]); - expect(listControl.hasValue()).toBe(true); - }); -}); + test('should be false when control has no value', () => { + expect(listControl.hasValue()).toBe(false); + }); -describe('fetch', () => { - const controlParams: ControlParams = { - id: '1', - fieldName: 'myField', - options: {} as any, - type: CONTROL_TYPES.LIST, - label: 'test', - indexPattern: {} as any, - parent: 'parent', - }; - const useTimeFilter = false; - - let listControl: ListControl; - beforeEach(async () => { - listControl = await listControlFactory(controlParams, useTimeFilter, MockSearchSource, deps); - }); + test('should be true when control has value', () => { + listControl.set([{ value: 'selected option', label: 'selection option' }]); + expect(listControl.hasValue()).toBe(true); + }); - test('should pass in timeout parameters from injected vars', async () => { - await listControl.fetch(); - expect(MockSearchSource).toHaveBeenCalledWith({ - timeout: `1000ms`, - terminate_after: 100000, + test('should be true when control has value that is the string "false"', () => { + listControl.set([{ value: 'false', label: 'selection option' }]); + expect(listControl.hasValue()).toBe(true); }); }); - test('should set selectOptions to results of terms aggregation', async () => { - await listControl.fetch(); - expect(listControl.selectOptions).toEqual([ - 'Zurich Airport', - 'Xi an Xianyang International Airport', - ]); - }); -}); - -describe('fetch with ancestors', () => { - const controlParams: ControlParams = { - id: '1', - fieldName: 'myField', - options: {} as any, - type: CONTROL_TYPES.LIST, - label: 'test', - indexPattern: {} as any, - parent: 'parent', - }; - const useTimeFilter = false; - - let listControl: ListControl; - let parentControl; - beforeEach(async () => { - listControl = await listControlFactory(controlParams, useTimeFilter, MockSearchSource, deps); - - const parentControlParams: ControlParams = { - id: 'parent', + describe('fetch', () => { + const controlParams: ControlParams = { + id: '1', fieldName: 'myField', options: {} as any, type: CONTROL_TYPES.LIST, @@ -120,26 +71,72 @@ describe('fetch with ancestors', () => { indexPattern: {} as any, parent: 'parent', }; - parentControl = await listControlFactory( - parentControlParams, - useTimeFilter, - MockSearchSource, - deps - ); - parentControl.clear(); - listControl.setAncestors([parentControl]); - }); + const useTimeFilter = false; + + let listControl: ListControl; + beforeEach(async () => { + listControl = await listControlFactory(controlParams, useTimeFilter, deps); + }); - describe('ancestor does not have value', () => { - test('should disable control', async () => { + test('should pass in timeout parameters from injected vars', async () => { await listControl.fetch(); - expect(listControl.isEnabled()).toBe(false); + expect(searchSourceMock).toHaveBeenCalledWith({ + timeout: `1000ms`, + terminate_after: 100000, + }); }); - test('should reset lastAncestorValues', async () => { - listControl.lastAncestorValues = 'last ancestor value'; + test('should set selectOptions to results of terms aggregation', async () => { await listControl.fetch(); - expect(listControl.lastAncestorValues).toBeUndefined(); + expect(listControl.selectOptions).toEqual([ + 'Zurich Airport', + 'Xi an Xianyang International Airport', + ]); + }); + }); + + describe('fetch with ancestors', () => { + const controlParams: ControlParams = { + id: '1', + fieldName: 'myField', + options: {} as any, + type: CONTROL_TYPES.LIST, + label: 'test', + indexPattern: {} as any, + parent: 'parent', + }; + const useTimeFilter = false; + + let listControl: ListControl; + let parentControl; + beforeEach(async () => { + listControl = await listControlFactory(controlParams, useTimeFilter, deps); + + const parentControlParams: ControlParams = { + id: 'parent', + fieldName: 'myField', + options: {} as any, + type: CONTROL_TYPES.LIST, + label: 'test', + indexPattern: {} as any, + parent: 'parent', + }; + parentControl = await listControlFactory(parentControlParams, useTimeFilter, deps); + parentControl.clear(); + listControl.setAncestors([parentControl]); + }); + + describe('ancestor does not have value', () => { + test('should disable control', async () => { + await listControl.fetch(); + expect(listControl.isEnabled()).toBe(false); + }); + + test('should reset lastAncestorValues', async () => { + listControl.lastAncestorValues = 'last ancestor value'; + await listControl.fetch(); + expect(listControl.lastAncestorValues).toBeUndefined(); + }); }); }); }); diff --git a/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.ts b/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.ts index 8364c82efecdb..4b2b1d751ffc7 100644 --- a/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.ts +++ b/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.ts @@ -19,14 +19,17 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; - -import { SearchSource as SearchSourceClass, SearchSourceFields } from '../legacy_imports'; import { Control, noValuesDisableMsg, noIndexPatternMsg } from './control'; import { PhraseFilterManager } from './filter_manager/phrase_filter_manager'; import { createSearchSource } from './create_search_source'; import { ControlParams } from '../editor_utils'; import { InputControlVisDependencies } from '../plugin'; -import { IFieldType, TimefilterContract } from '../../../../../plugins/data/public'; +import { + IFieldType, + TimefilterContract, + SearchSourceFields, + DataPublicPluginStart, +} from '../../../../../plugins/data/public'; function getEscapedQuery(query = '') { // https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-regexp-query.html#_standard_operators @@ -75,6 +78,7 @@ const termsAgg = ({ field, size, direction, query }: TermsAggArgs) => { export class ListControl extends Control { private getInjectedVar: InputControlVisDependencies['core']['injectedMetadata']['getInjectedVar']; private timefilter: TimefilterContract; + private searchSource: DataPublicPluginStart['search']['searchSource']; abortController?: AbortController; lastAncestorValues: any; @@ -86,12 +90,13 @@ export class ListControl extends Control { controlParams: ControlParams, filterManager: PhraseFilterManager, useTimeFilter: boolean, - SearchSource: SearchSourceClass, + searchSource: DataPublicPluginStart['search']['searchSource'], deps: InputControlVisDependencies ) { - super(controlParams, filterManager, useTimeFilter, SearchSource); + super(controlParams, filterManager, useTimeFilter); this.getInjectedVar = deps.core.injectedMetadata.getInjectedVar; this.timefilter = deps.data.query.timefilter.timefilter; + this.searchSource = searchSource; } fetch = async (query?: string) => { @@ -143,7 +148,7 @@ export class ListControl extends Control { query, }); const searchSource = createSearchSource( - this.SearchSource, + this.searchSource, initialSearchSourceState, indexPattern, aggs, @@ -202,7 +207,6 @@ export class ListControl extends Control { export async function listControlFactory( controlParams: ControlParams, useTimeFilter: boolean, - SearchSource: SearchSourceClass, deps: InputControlVisDependencies ) { const [, { data: dataPluginStart }] = await deps.core.getStartServices(); @@ -225,7 +229,7 @@ export async function listControlFactory( deps.data.query.filterManager ), useTimeFilter, - SearchSource, + dataPluginStart.search.searchSource, deps ); return listControl; diff --git a/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.test.ts b/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.test.ts index 32df9de8ac983..084c02e138a2d 100644 --- a/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.test.ts +++ b/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.test.ts @@ -21,71 +21,77 @@ import { rangeControlFactory } from './range_control_factory'; import { ControlParams, CONTROL_TYPES } from '../editor_utils'; import { getDepsMock, getSearchSourceMock } from '../test_utils'; -const deps = getDepsMock(); +describe('rangeControlFactory', () => { + describe('fetch', () => { + const controlParams: ControlParams = { + id: '1', + fieldName: 'myNumberField', + options: {}, + type: CONTROL_TYPES.RANGE, + label: 'test', + indexPattern: {} as any, + parent: {} as any, + }; + const useTimeFilter = false; -describe('fetch', () => { - const controlParams: ControlParams = { - id: '1', - fieldName: 'myNumberField', - options: {}, - type: CONTROL_TYPES.RANGE, - label: 'test', - indexPattern: {} as any, - parent: {} as any, - }; - const useTimeFilter = false; + test('should set min and max from aggregation results', async () => { + const esSearchResponse = { + aggregations: { + maxAgg: { value: 100 }, + minAgg: { value: 10 }, + }, + }; + const searchSourceMock = getSearchSourceMock(esSearchResponse); + const deps = getDepsMock({ + searchSource: { + create: searchSourceMock, + }, + }); - test('should set min and max from aggregation results', async () => { - const esSearchResponse = { - aggregations: { - maxAgg: { value: 100 }, - minAgg: { value: 10 }, - }, - }; - const rangeControl = await rangeControlFactory( - controlParams, - useTimeFilter, - getSearchSourceMock(esSearchResponse), - deps - ); - await rangeControl.fetch(); + const rangeControl = await rangeControlFactory(controlParams, useTimeFilter, deps); + await rangeControl.fetch(); - expect(rangeControl.isEnabled()).toBe(true); - expect(rangeControl.min).toBe(10); - expect(rangeControl.max).toBe(100); - }); + expect(rangeControl.isEnabled()).toBe(true); + expect(rangeControl.min).toBe(10); + expect(rangeControl.max).toBe(100); + }); - test('should disable control when there are 0 hits', async () => { - // ES response when the query does not match any documents - const esSearchResponse = { - aggregations: { - maxAgg: { value: null }, - minAgg: { value: null }, - }, - }; - const rangeControl = await rangeControlFactory( - controlParams, - useTimeFilter, - getSearchSourceMock(esSearchResponse), - deps - ); - await rangeControl.fetch(); + test('should disable control when there are 0 hits', async () => { + // ES response when the query does not match any documents + const esSearchResponse = { + aggregations: { + maxAgg: { value: null }, + minAgg: { value: null }, + }, + }; + const searchSourceMock = getSearchSourceMock(esSearchResponse); + const deps = getDepsMock({ + searchSource: { + create: searchSourceMock, + }, + }); - expect(rangeControl.isEnabled()).toBe(false); - }); + const rangeControl = await rangeControlFactory(controlParams, useTimeFilter, deps); + await rangeControl.fetch(); + + expect(rangeControl.isEnabled()).toBe(false); + }); + + test('should disable control when response is empty', async () => { + // ES response for dashboardonly user who does not have read permissions on index is 200 (which is weird) + // and there is not aggregations key + const esSearchResponse = {}; + const searchSourceMock = getSearchSourceMock(esSearchResponse); + const deps = getDepsMock({ + searchSource: { + create: searchSourceMock, + }, + }); - test('should disable control when response is empty', async () => { - // ES response for dashboardonly user who does not have read permissions on index is 200 (which is weird) - // and there is not aggregations key - const esSearchResponse = {}; - const rangeControl = await rangeControlFactory( - controlParams, - useTimeFilter, - getSearchSourceMock(esSearchResponse), - deps - ); - await rangeControl.fetch(); + const rangeControl = await rangeControlFactory(controlParams, useTimeFilter, deps); + await rangeControl.fetch(); - expect(rangeControl.isEnabled()).toBe(false); + expect(rangeControl.isEnabled()).toBe(false); + }); }); }); diff --git a/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.ts b/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.ts index d9b43c9dff201..5f3c9994ef353 100644 --- a/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.ts +++ b/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.ts @@ -20,13 +20,16 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; -import { SearchSource as SearchSourceClass } from '../legacy_imports'; import { Control, noValuesDisableMsg, noIndexPatternMsg } from './control'; import { RangeFilterManager } from './filter_manager/range_filter_manager'; import { createSearchSource } from './create_search_source'; import { ControlParams } from '../editor_utils'; import { InputControlVisDependencies } from '../plugin'; -import { IFieldType, TimefilterContract } from '../.../../../../../../plugins/data/public'; +import { + IFieldType, + TimefilterContract, + DataPublicPluginStart, +} from '../.../../../../../../plugins/data/public'; const minMaxAgg = (field?: IFieldType) => { const aggBody: any = {}; @@ -52,6 +55,8 @@ const minMaxAgg = (field?: IFieldType) => { }; export class RangeControl extends Control { + private searchSource: DataPublicPluginStart['search']['searchSource']; + timefilter: TimefilterContract; abortController: any; min: any; @@ -61,11 +66,12 @@ export class RangeControl extends Control { controlParams: ControlParams, filterManager: RangeFilterManager, useTimeFilter: boolean, - SearchSource: SearchSourceClass, + searchSource: DataPublicPluginStart['search']['searchSource'], deps: InputControlVisDependencies ) { - super(controlParams, filterManager, useTimeFilter, SearchSource); + super(controlParams, filterManager, useTimeFilter); this.timefilter = deps.data.query.timefilter.timefilter; + this.searchSource = searchSource; } async fetch() { @@ -83,7 +89,7 @@ export class RangeControl extends Control { const fieldName = this.filterManager.fieldName; const aggs = minMaxAgg(indexPattern.fields.getByName(fieldName)); const searchSource = createSearchSource( - this.SearchSource, + this.searchSource, null, indexPattern, aggs, @@ -129,7 +135,6 @@ export class RangeControl extends Control { export async function rangeControlFactory( controlParams: ControlParams, useTimeFilter: boolean, - SearchSource: SearchSourceClass, deps: InputControlVisDependencies ): Promise { const [, { data: dataPluginStart }] = await deps.core.getStartServices(); @@ -144,7 +149,7 @@ export async function rangeControlFactory( deps.data.query.filterManager ), useTimeFilter, - SearchSource, + dataPluginStart.search.searchSource, deps ); } diff --git a/src/legacy/core_plugins/input_control_vis/public/input_control_fn.test.ts b/src/legacy/core_plugins/input_control_vis/public/input_control_fn.test.ts index acc214ed31180..d654acefd0550 100644 --- a/src/legacy/core_plugins/input_control_vis/public/input_control_fn.test.ts +++ b/src/legacy/core_plugins/input_control_vis/public/input_control_fn.test.ts @@ -22,8 +22,6 @@ import { createInputControlVisFn } from './input_control_fn'; // eslint-disable-next-line import { functionWrapper } from '../../../../plugins/expressions/common/expression_functions/specs/tests/utils'; -jest.mock('./legacy_imports.ts'); - describe('interpreter/functions#input_control_vis', () => { const fn = functionWrapper(createInputControlVisFn()); const visConfig = { @@ -48,8 +46,9 @@ describe('interpreter/functions#input_control_vis', () => { pinFilters: false, }; - it('returns an object with the correct structure', async () => { + test('returns an object with the correct structure', async () => { const actual = await fn(null, { visConfig: JSON.stringify(visConfig) }); + expect(actual).toMatchSnapshot(); }); }); diff --git a/src/legacy/core_plugins/input_control_vis/public/legacy_imports.ts b/src/legacy/core_plugins/input_control_vis/public/legacy_imports.ts deleted file mode 100644 index 8c58ac2386da4..0000000000000 --- a/src/legacy/core_plugins/input_control_vis/public/legacy_imports.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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 { Class } from '@kbn/utility-types'; -import { SearchSource as SearchSourceClass, ISearchSource } from '../../../../plugins/data/public'; - -export { SearchSourceFields } from '../../../../plugins/data/public'; - -export type SearchSource = Class; -export const SearchSource = SearchSourceClass; diff --git a/src/legacy/core_plugins/input_control_vis/public/test_utils/get_deps_mock.tsx b/src/legacy/core_plugins/input_control_vis/public/test_utils/get_deps_mock.tsx index 78a4ef3a5597a..efd7d4c020854 100644 --- a/src/legacy/core_plugins/input_control_vis/public/test_utils/get_deps_mock.tsx +++ b/src/legacy/core_plugins/input_control_vis/public/test_utils/get_deps_mock.tsx @@ -19,6 +19,7 @@ import React from 'react'; import { InputControlVisDependencies } from '../plugin'; +import { getSearchSourceMock } from './get_search_service_mock'; const fields = [] as any; fields.push({ name: 'myField' } as any); @@ -26,13 +27,20 @@ fields.getByName = (name: any) => { return fields.find(({ name: n }: { name: string }) => n === name); }; -export const getDepsMock = (): InputControlVisDependencies => +export const getDepsMock = ({ + searchSource = { + create: getSearchSourceMock(), + }, +} = {}): InputControlVisDependencies => ({ core: { getStartServices: jest.fn().mockReturnValue([ null, { data: { + search: { + searchSource, + }, ui: { IndexPatternSelect: () => (
) as any, }, @@ -58,6 +66,11 @@ export const getDepsMock = (): InputControlVisDependencies => }, }, data: { + search: { + searchSource: { + create: getSearchSourceMock(), + }, + }, query: { filterManager: { fieldName: 'myField', diff --git a/src/legacy/core_plugins/input_control_vis/public/test_utils/get_search_service_mock.ts b/src/legacy/core_plugins/input_control_vis/public/test_utils/get_search_service_mock.ts index 94a460086e9da..24b7d7bcbb5c1 100644 --- a/src/legacy/core_plugins/input_control_vis/public/test_utils/get_search_service_mock.ts +++ b/src/legacy/core_plugins/input_control_vis/public/test_utils/get_search_service_mock.ts @@ -17,9 +17,7 @@ * under the License. */ -import { SearchSource } from '../legacy_imports'; - -export const getSearchSourceMock = (esSearchResponse?: any): SearchSource => +export const getSearchSourceMock = (esSearchResponse?: any) => jest.fn().mockImplementation(() => ({ setParent: jest.fn(), setField: jest.fn(), diff --git a/src/legacy/core_plugins/input_control_vis/public/vis_controller.tsx b/src/legacy/core_plugins/input_control_vis/public/vis_controller.tsx index c4a7d286850e3..818221353afbc 100644 --- a/src/legacy/core_plugins/input_control_vis/public/vis_controller.tsx +++ b/src/legacy/core_plugins/input_control_vis/public/vis_controller.tsx @@ -21,8 +21,6 @@ import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { I18nStart } from 'kibana/public'; -import { SearchSource } from './legacy_imports'; - import { InputControlVis } from './components/vis/input_control_vis'; import { getControlFactory } from './control/control_factory'; import { getLineageMap } from './lineage'; @@ -102,7 +100,8 @@ export const createInputControlVisController = (deps: InputControlVisDependencie const controlFactoryPromises = controlParamsList.map(controlParams => { const factory = getControlFactory(controlParams); - return factory(controlParams, this.visParams?.useTimeFilter, SearchSource, deps); + + return factory(controlParams, this.visParams?.useTimeFilter, deps); }); const controls = await Promise.all(controlFactoryPromises); diff --git a/src/legacy/core_plugins/kibana/public/discover/build_services.ts b/src/legacy/core_plugins/kibana/public/discover/build_services.ts index c56e50f3b27ff..b987129a9a7ed 100644 --- a/src/legacy/core_plugins/kibana/public/discover/build_services.ts +++ b/src/legacy/core_plugins/kibana/public/discover/build_services.ts @@ -42,6 +42,7 @@ import { DocViewerComponent, SavedSearch, } from '../../../../../plugins/discover/public'; +import { SavedObjectKibanaServices } from '../../../../../plugins/saved_objects/public'; export interface DiscoverServices { addBasePath: (path: string) => string; @@ -65,12 +66,13 @@ export interface DiscoverServices { uiSettings: IUiSettingsClient; visualizations: VisualizationsStart; } + export async function buildServices( core: CoreStart, plugins: DiscoverStartPlugins, getHistory: () => History ): Promise { - const services = { + const services: SavedObjectKibanaServices = { savedObjectsClient: core.savedObjects.client, indexPatterns: plugins.data.indexPatterns, search: plugins.data.search, diff --git a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts index 156267bdfa87e..3aa552b1da07d 100644 --- a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts @@ -77,7 +77,6 @@ export { IndexPattern, indexPatterns, IFieldType, - SearchSource, ISearchSource, EsQuerySortValue, SortDirection, diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/_stubs.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/_stubs.js index f6ed570be2c37..acd609df203e3 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/_stubs.js +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/_stubs.js @@ -19,7 +19,6 @@ import sinon from 'sinon'; import moment from 'moment'; -import { SearchSource } from '../../../../../../../../../plugins/data/public'; export function createIndexPatternsStub() { return { @@ -46,17 +45,15 @@ export function createSearchSourceStub(hits, timeField) { }), }; - searchSourceStub.setParent = sinon - .stub(SearchSource.prototype, 'setParent') - .returns(searchSourceStub); - searchSourceStub.setField = sinon - .stub(SearchSource.prototype, 'setField') - .returns(searchSourceStub); - searchSourceStub.getField = sinon.stub(SearchSource.prototype, 'getField').callsFake(key => { + searchSourceStub.setParent = sinon.spy(() => searchSourceStub); + searchSourceStub.setField = sinon.spy(() => searchSourceStub); + + searchSourceStub.getField = sinon.spy(key => { const previousSetCall = searchSourceStub.setField.withArgs(key).lastCall; return previousSetCall ? previousSetCall.args[1] : null; }); - searchSourceStub.fetch = sinon.stub(SearchSource.prototype, 'fetch').callsFake(() => + + searchSourceStub.fetch = sinon.spy(() => Promise.resolve({ hits: { hits: searchSourceStub._stubHits, @@ -65,13 +62,6 @@ export function createSearchSourceStub(hits, timeField) { }) ); - searchSourceStub._restore = () => { - searchSourceStub.setParent.restore(); - searchSourceStub.setField.restore(); - searchSourceStub.getField.restore(); - searchSourceStub.fetch.restore(); - }; - return searchSourceStub; } @@ -81,8 +71,7 @@ export function createSearchSourceStub(hits, timeField) { export function createContextSearchSourceStub(hits, timeField = '@timestamp') { const searchSourceStub = createSearchSourceStub(hits, timeField); - searchSourceStub.fetch.restore(); - searchSourceStub.fetch = sinon.stub(SearchSource.prototype, 'fetch').callsFake(() => { + searchSourceStub.fetch = sinon.spy(() => { const timeField = searchSourceStub._stubTimeField; const lastQuery = searchSourceStub.setField.withArgs('query').lastCall.args[1]; const timeRange = lastQuery.query.constant_score.filter.range[timeField]; @@ -99,6 +88,7 @@ export function createContextSearchSourceStub(hits, timeField = '@timestamp') { moment(hit[timeField]).isSameOrBefore(timeRange.lte) ) .sort(sortFunction); + return Promise.resolve({ hits: { hits: filteredHits, diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/anchor.test.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/anchor.test.js index 0bc2cbacc1eee..757e74589555a 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/anchor.test.js +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/anchor.test.js @@ -31,10 +31,6 @@ describe('context app', function() { fetchAnchor = fetchAnchorProvider(createIndexPatternsStub(), searchSourceStub); }); - afterEach(() => { - searchSourceStub._restore(); - }); - it('should use the `fetch` method of the SearchSource', function() { return fetchAnchor('INDEX_PATTERN_ID', 'id', [ { '@timestamp': 'desc' }, diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.predecessors.test.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.predecessors.test.js index d6e91e57b22a8..ebd4af536aabd 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.predecessors.test.js +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.predecessors.test.js @@ -21,6 +21,7 @@ import moment from 'moment'; import * as _ from 'lodash'; import { createIndexPatternsStub, createContextSearchSourceStub } from './_stubs'; import { fetchContextProvider } from './context'; +import { setServices } from '../../../../kibana_services'; const MS_PER_DAY = 24 * 60 * 60 * 1000; const ANCHOR_TIMESTAMP = new Date(MS_PER_DAY).toJSON(); @@ -31,10 +32,21 @@ const ANCHOR_TIMESTAMP_3000 = new Date(MS_PER_DAY * 3000).toJSON(); describe('context app', function() { describe('function fetchPredecessors', function() { let fetchPredecessors; - let searchSourceStub; + let mockSearchSource; beforeEach(() => { - searchSourceStub = createContextSearchSourceStub([], '@timestamp', MS_PER_DAY * 8); + mockSearchSource = createContextSearchSourceStub([], '@timestamp', MS_PER_DAY * 8); + + setServices({ + data: { + search: { + searchSource: { + create: jest.fn().mockImplementation(() => mockSearchSource), + }, + }, + }, + }); + fetchPredecessors = ( indexPatternId, timeField, @@ -65,17 +77,13 @@ describe('context app', function() { }; }); - afterEach(() => { - searchSourceStub._restore(); - }); - it('should perform exactly one query when enough hits are returned', function() { - searchSourceStub._stubHits = [ - searchSourceStub._createStubHit(MS_PER_DAY * 3000 + 2), - searchSourceStub._createStubHit(MS_PER_DAY * 3000 + 1), - searchSourceStub._createStubHit(MS_PER_DAY * 3000), - searchSourceStub._createStubHit(MS_PER_DAY * 2000), - searchSourceStub._createStubHit(MS_PER_DAY * 1000), + mockSearchSource._stubHits = [ + mockSearchSource._createStubHit(MS_PER_DAY * 3000 + 2), + mockSearchSource._createStubHit(MS_PER_DAY * 3000 + 1), + mockSearchSource._createStubHit(MS_PER_DAY * 3000), + mockSearchSource._createStubHit(MS_PER_DAY * 2000), + mockSearchSource._createStubHit(MS_PER_DAY * 1000), ]; return fetchPredecessors( @@ -89,18 +97,18 @@ describe('context app', function() { 3, [] ).then(hits => { - expect(searchSourceStub.fetch.calledOnce).toBe(true); - expect(hits).toEqual(searchSourceStub._stubHits.slice(0, 3)); + expect(mockSearchSource.fetch.calledOnce).toBe(true); + expect(hits).toEqual(mockSearchSource._stubHits.slice(0, 3)); }); }); it('should perform multiple queries with the last being unrestricted when too few hits are returned', function() { - searchSourceStub._stubHits = [ - searchSourceStub._createStubHit(MS_PER_DAY * 3010), - searchSourceStub._createStubHit(MS_PER_DAY * 3002), - searchSourceStub._createStubHit(MS_PER_DAY * 3000), - searchSourceStub._createStubHit(MS_PER_DAY * 2998), - searchSourceStub._createStubHit(MS_PER_DAY * 2990), + mockSearchSource._stubHits = [ + mockSearchSource._createStubHit(MS_PER_DAY * 3010), + mockSearchSource._createStubHit(MS_PER_DAY * 3002), + mockSearchSource._createStubHit(MS_PER_DAY * 3000), + mockSearchSource._createStubHit(MS_PER_DAY * 2998), + mockSearchSource._createStubHit(MS_PER_DAY * 2990), ]; return fetchPredecessors( @@ -114,7 +122,7 @@ describe('context app', function() { 6, [] ).then(hits => { - const intervals = searchSourceStub.setField.args + const intervals = mockSearchSource.setField.args .filter(([property]) => property === 'query') .map(([, { query }]) => _.get(query, ['constant_score', 'filter', 'range', '@timestamp']) @@ -129,16 +137,16 @@ describe('context app', function() { expect(Object.keys(_.last(intervals))).toEqual(['format', 'gte']); expect(intervals.length).toBeGreaterThan(1); - expect(hits).toEqual(searchSourceStub._stubHits.slice(0, 3)); + expect(hits).toEqual(mockSearchSource._stubHits.slice(0, 3)); }); }); it('should perform multiple queries until the expected hit count is returned', function() { - searchSourceStub._stubHits = [ - searchSourceStub._createStubHit(MS_PER_DAY * 1700), - searchSourceStub._createStubHit(MS_PER_DAY * 1200), - searchSourceStub._createStubHit(MS_PER_DAY * 1100), - searchSourceStub._createStubHit(MS_PER_DAY * 1000), + mockSearchSource._stubHits = [ + mockSearchSource._createStubHit(MS_PER_DAY * 1700), + mockSearchSource._createStubHit(MS_PER_DAY * 1200), + mockSearchSource._createStubHit(MS_PER_DAY * 1100), + mockSearchSource._createStubHit(MS_PER_DAY * 1000), ]; return fetchPredecessors( @@ -152,7 +160,7 @@ describe('context app', function() { 3, [] ).then(hits => { - const intervals = searchSourceStub.setField.args + const intervals = mockSearchSource.setField.args .filter(([property]) => property === 'query') .map(([, { query }]) => _.get(query, ['constant_score', 'filter', 'range', '@timestamp']) @@ -163,7 +171,7 @@ describe('context app', function() { // should have stopped before reaching MS_PER_DAY * 1700 expect(moment(_.last(intervals).lte).valueOf()).toBeLessThan(MS_PER_DAY * 1700); expect(intervals.length).toBeGreaterThan(1); - expect(hits).toEqual(searchSourceStub._stubHits.slice(-3)); + expect(hits).toEqual(mockSearchSource._stubHits.slice(-3)); }); }); @@ -195,7 +203,7 @@ describe('context app', function() { 3, [] ).then(() => { - const setParentSpy = searchSourceStub.setParent; + const setParentSpy = mockSearchSource.setParent; expect(setParentSpy.alwaysCalledWith(undefined)).toBe(true); expect(setParentSpy.called).toBe(true); }); @@ -214,7 +222,7 @@ describe('context app', function() { [] ).then(() => { expect( - searchSourceStub.setField.calledWith('sort', [{ '@timestamp': 'asc' }, { _doc: 'asc' }]) + mockSearchSource.setField.calledWith('sort', [{ '@timestamp': 'asc' }, { _doc: 'asc' }]) ).toBe(true); }); }); diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.successors.test.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.successors.test.js index cc2b6d31cb43b..452d0cc9fd1a0 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.successors.test.js +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.successors.test.js @@ -21,6 +21,7 @@ import moment from 'moment'; import * as _ from 'lodash'; import { createIndexPatternsStub, createContextSearchSourceStub } from './_stubs'; +import { setServices } from '../../../../kibana_services'; import { fetchContextProvider } from './context'; @@ -32,10 +33,20 @@ const ANCHOR_TIMESTAMP_3000 = new Date(MS_PER_DAY * 3000).toJSON(); describe('context app', function() { describe('function fetchSuccessors', function() { let fetchSuccessors; - let searchSourceStub; + let mockSearchSource; beforeEach(() => { - searchSourceStub = createContextSearchSourceStub([], '@timestamp'); + mockSearchSource = createContextSearchSourceStub([], '@timestamp'); + + setServices({ + data: { + search: { + searchSource: { + create: jest.fn().mockImplementation(() => mockSearchSource), + }, + }, + }, + }); fetchSuccessors = ( indexPatternId, @@ -67,17 +78,13 @@ describe('context app', function() { }; }); - afterEach(() => { - searchSourceStub._restore(); - }); - it('should perform exactly one query when enough hits are returned', function() { - searchSourceStub._stubHits = [ - searchSourceStub._createStubHit(MS_PER_DAY * 5000), - searchSourceStub._createStubHit(MS_PER_DAY * 4000), - searchSourceStub._createStubHit(MS_PER_DAY * 3000), - searchSourceStub._createStubHit(MS_PER_DAY * 3000 - 1), - searchSourceStub._createStubHit(MS_PER_DAY * 3000 - 2), + mockSearchSource._stubHits = [ + mockSearchSource._createStubHit(MS_PER_DAY * 5000), + mockSearchSource._createStubHit(MS_PER_DAY * 4000), + mockSearchSource._createStubHit(MS_PER_DAY * 3000), + mockSearchSource._createStubHit(MS_PER_DAY * 3000 - 1), + mockSearchSource._createStubHit(MS_PER_DAY * 3000 - 2), ]; return fetchSuccessors( @@ -91,18 +98,18 @@ describe('context app', function() { 3, [] ).then(hits => { - expect(searchSourceStub.fetch.calledOnce).toBe(true); - expect(hits).toEqual(searchSourceStub._stubHits.slice(-3)); + expect(mockSearchSource.fetch.calledOnce).toBe(true); + expect(hits).toEqual(mockSearchSource._stubHits.slice(-3)); }); }); it('should perform multiple queries with the last being unrestricted when too few hits are returned', function() { - searchSourceStub._stubHits = [ - searchSourceStub._createStubHit(MS_PER_DAY * 3010), - searchSourceStub._createStubHit(MS_PER_DAY * 3002), - searchSourceStub._createStubHit(MS_PER_DAY * 3000), - searchSourceStub._createStubHit(MS_PER_DAY * 2998), - searchSourceStub._createStubHit(MS_PER_DAY * 2990), + mockSearchSource._stubHits = [ + mockSearchSource._createStubHit(MS_PER_DAY * 3010), + mockSearchSource._createStubHit(MS_PER_DAY * 3002), + mockSearchSource._createStubHit(MS_PER_DAY * 3000), + mockSearchSource._createStubHit(MS_PER_DAY * 2998), + mockSearchSource._createStubHit(MS_PER_DAY * 2990), ]; return fetchSuccessors( @@ -116,7 +123,7 @@ describe('context app', function() { 6, [] ).then(hits => { - const intervals = searchSourceStub.setField.args + const intervals = mockSearchSource.setField.args .filter(([property]) => property === 'query') .map(([, { query }]) => _.get(query, ['constant_score', 'filter', 'range', '@timestamp']) @@ -131,18 +138,18 @@ describe('context app', function() { expect(Object.keys(_.last(intervals))).toEqual(['format', 'lte']); expect(intervals.length).toBeGreaterThan(1); - expect(hits).toEqual(searchSourceStub._stubHits.slice(-3)); + expect(hits).toEqual(mockSearchSource._stubHits.slice(-3)); }); }); it('should perform multiple queries until the expected hit count is returned', function() { - searchSourceStub._stubHits = [ - searchSourceStub._createStubHit(MS_PER_DAY * 3000), - searchSourceStub._createStubHit(MS_PER_DAY * 3000 - 1), - searchSourceStub._createStubHit(MS_PER_DAY * 3000 - 2), - searchSourceStub._createStubHit(MS_PER_DAY * 2800), - searchSourceStub._createStubHit(MS_PER_DAY * 2200), - searchSourceStub._createStubHit(MS_PER_DAY * 1000), + mockSearchSource._stubHits = [ + mockSearchSource._createStubHit(MS_PER_DAY * 3000), + mockSearchSource._createStubHit(MS_PER_DAY * 3000 - 1), + mockSearchSource._createStubHit(MS_PER_DAY * 3000 - 2), + mockSearchSource._createStubHit(MS_PER_DAY * 2800), + mockSearchSource._createStubHit(MS_PER_DAY * 2200), + mockSearchSource._createStubHit(MS_PER_DAY * 1000), ]; return fetchSuccessors( @@ -156,7 +163,7 @@ describe('context app', function() { 4, [] ).then(hits => { - const intervals = searchSourceStub.setField.args + const intervals = mockSearchSource.setField.args .filter(([property]) => property === 'query') .map(([, { query }]) => _.get(query, ['constant_score', 'filter', 'range', '@timestamp']) @@ -168,7 +175,7 @@ describe('context app', function() { expect(moment(_.last(intervals).gte).valueOf()).toBeGreaterThan(MS_PER_DAY * 2200); expect(intervals.length).toBeGreaterThan(1); - expect(hits).toEqual(searchSourceStub._stubHits.slice(0, 4)); + expect(hits).toEqual(mockSearchSource._stubHits.slice(0, 4)); }); }); @@ -200,7 +207,7 @@ describe('context app', function() { 3, [] ).then(() => { - const setParentSpy = searchSourceStub.setParent; + const setParentSpy = mockSearchSource.setParent; expect(setParentSpy.alwaysCalledWith(undefined)).toBe(true); expect(setParentSpy.called).toBe(true); }); @@ -219,7 +226,7 @@ describe('context app', function() { [] ).then(() => { expect( - searchSourceStub.setField.calledWith('sort', [{ '@timestamp': 'desc' }, { _doc: 'desc' }]) + mockSearchSource.setField.calledWith('sort', [{ '@timestamp': 'desc' }, { _doc: 'desc' }]) ).toBe(true); }); }); diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.ts index 507f927c608e1..2760eec38755e 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.ts +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.ts @@ -27,8 +27,8 @@ import { Filter, IndexPatternsContract, IndexPattern, - SearchSource, } from '../../../../../../../../../plugins/data/public'; +import { getServices } from '../../../../kibana_services'; export type SurrDocType = 'successors' | 'predecessors'; export interface EsHitRecord { @@ -115,7 +115,10 @@ function fetchContextProvider(indexPatterns: IndexPatternsContract) { } async function createSearchSource(indexPattern: IndexPattern, filters: Filter[]) { - return new SearchSource() + const { data } = getServices(); + + return data.search.searchSource + .create() .setParent(undefined) .setField('index', indexPattern) .setField('filter', filters); diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query/actions.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query/actions.js index 9efddc5275069..efc230d2cd4ae 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query/actions.js +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query/actions.js @@ -20,7 +20,7 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { getServices, SearchSource } from '../../../../kibana_services'; +import { getServices } from '../../../../kibana_services'; import { fetchAnchorProvider } from '../api/anchor'; import { fetchContextProvider } from '../api/context'; @@ -29,8 +29,8 @@ import { FAILURE_REASONS, LOADING_STATUS } from './constants'; import { MarkdownSimple } from '../../../../../../../../../plugins/kibana_react/public'; export function QueryActionsProvider(Promise) { - const { filterManager, indexPatterns } = getServices(); - const fetchAnchor = fetchAnchorProvider(indexPatterns, new SearchSource()); + const { filterManager, indexPatterns, data } = getServices(); + const fetchAnchor = fetchAnchorProvider(indexPatterns, data.search.searchSource.create()); const { fetchSurroundingDocs } = fetchContextProvider(indexPatterns); const { setPredecessorCount, setQueryParameters, setSuccessorCount } = getQueryParameterActions( filterManager, diff --git a/src/plugins/dashboard/public/application/test_helpers/get_saved_dashboard_mock.ts b/src/plugins/dashboard/public/application/test_helpers/get_saved_dashboard_mock.ts index 57c147ffe3588..ee59c68cce451 100644 --- a/src/plugins/dashboard/public/application/test_helpers/get_saved_dashboard_mock.ts +++ b/src/plugins/dashboard/public/application/test_helpers/get_saved_dashboard_mock.ts @@ -17,17 +17,19 @@ * under the License. */ -import { searchSourceMock } from '../../../../data/public/mocks'; +import { dataPluginMock } from '../../../../data/public/mocks'; import { SavedObjectDashboard } from '../../saved_dashboards'; export function getSavedDashboardMock( config?: Partial ): SavedObjectDashboard { + const searchSource = dataPluginMock.createStartContract(); + return { id: '123', title: 'my dashboard', panelsJSON: '[]', - searchSource: searchSourceMock, + searchSource: searchSource.search.searchSource.create(), copyOnSave: false, timeRestore: false, timeTo: 'now', diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 05a4141483587..e1e2576b2a0e7 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -365,8 +365,6 @@ export { SearchResponse, SearchError, ISearchSource, - SearchSource, - createSearchSource, SearchSourceFields, EsQuerySortValue, SortDirection, diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts index 1f604b9eb6baa..a2df754786a68 100644 --- a/src/plugins/data/public/mocks.ts +++ b/src/plugins/data/public/mocks.ts @@ -68,7 +68,7 @@ const createStartContract = (): Start => { }; }; -export { searchSourceMock } from './search/mocks'; +export { createSearchSourceMock } from './search/mocks'; export { getCalculateAutoTimeExpression } from './search/aggs'; export const dataPluginMock = { diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts index ccf94171235fe..49ef1000d7993 100644 --- a/src/plugins/data/public/plugin.ts +++ b/src/plugins/data/public/plugin.ts @@ -24,13 +24,13 @@ import { Plugin, PackageInfo, } from 'src/core/public'; -import { Storage, IStorageWrapper } from '../../kibana_utils/public'; +import { Storage, IStorageWrapper, createStartServicesGetter } from '../../kibana_utils/public'; import { DataPublicPluginSetup, DataPublicPluginStart, DataSetupDependencies, DataStartDependencies, - GetInternalStartServicesFn, + InternalStartServices, } from './types'; import { AutocompleteService } from './autocomplete'; import { SearchService } from './search/search_service'; @@ -48,8 +48,6 @@ import { setQueryService, setSearchService, setUiSettings, - getFieldFormats, - getNotifications, } from './services'; import { createSearchBar } from './ui/search_bar/create_search_bar'; import { esaggs } from './search/expressions'; @@ -104,15 +102,21 @@ export class DataPublicPlugin implements Plugin { + const { core: coreStart, self }: any = startServices(); + return { + fieldFormats: self.fieldFormats, + notifications: coreStart.notifications, + uiSettings: coreStart.uiSettings, + searchService: self.search, + injectedMetadata: coreStart.injectedMetadata, + }; + }; expressions.registerFunction(esaggs); - const getInternalStartServices: GetInternalStartServicesFn = () => ({ - fieldFormats: getFieldFormats(), - notifications: getNotifications(), - }); - const queryService = this.queryService.setup({ uiSettings: core.uiSettings, storage: this.storage, @@ -150,6 +154,7 @@ export class DataPublicPlugin implements Plugin({ timefilter: { timefil // @public (undocumented) export const createSavedQueryService: (savedObjectsClient: Pick) => SavedQueryService; -// @public -export const createSearchSource: (indexPatterns: Pick) => (searchSourceJson: string, references: SavedObjectReference[]) => Promise; - // Warning: (ae-missing-release-tag) "CustomFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -637,21 +634,21 @@ export type IAggType = AggType; // Warning: (ae-missing-release-tag) "IDataPluginServices" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export interface IDataPluginServices extends Partial { +export interface IDataPluginServices extends Partial { // (undocumented) appName: string; // (undocumented) data: DataPublicPluginStart; // (undocumented) - http: CoreStart['http']; + http: CoreStart_2['http']; // (undocumented) - notifications: CoreStart['notifications']; + notifications: CoreStart_2['notifications']; // (undocumented) - savedObjects: CoreStart['savedObjects']; + savedObjects: CoreStart_2['savedObjects']; // (undocumented) storage: IStorageWrapper; // (undocumented) - uiSettings: CoreStart['uiSettings']; + uiSettings: CoreStart_2['uiSettings']; } // Warning: (ae-missing-release-tag) "IEsSearchRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1099,7 +1096,7 @@ export type ISearch = // @public (undocumented) export interface ISearchContext { // (undocumented) - core: CoreStart_2; + core: CoreStart; // (undocumented) getSearchStrategy: (name: T) => TSearchStrategyProvider; } @@ -1117,7 +1114,7 @@ export interface ISearchOptions { signal?: AbortSignal; } -// Warning: (ae-missing-release-tag) "ISearchSource" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-forgotten-export) The symbol "SearchSource" needs to be exported by the entry point index.d.ts // // @public (undocumented) export type ISearchSource = Pick; @@ -1312,7 +1309,7 @@ export class Plugin implements Plugin_2; - getField(field: K, recurse?: boolean): SearchSourceFields[K]; - // (undocumented) - getFields(): { - type?: string | undefined; - query?: import("../..").Query | undefined; - filter?: Filter | Filter[] | (() => Filter | Filter[] | undefined) | undefined; - sort?: Record | Record[] | undefined; - highlight?: any; - highlightAll?: boolean | undefined; - aggs?: any; - from?: number | undefined; - size?: number | undefined; - source?: string | boolean | string[] | undefined; - version?: boolean | undefined; - fields?: string | boolean | string[] | undefined; - index?: import("../..").IndexPattern | undefined; - searchAfter?: import("./types").EsQuerySearchAfter | undefined; - timeout?: string | undefined; - terminate_after?: number | undefined; - }; - // (undocumented) - getId(): string; - getOwnField(field: K): SearchSourceFields[K]; - getParent(): SearchSource | undefined; - // (undocumented) - getSearchRequestBody(): Promise; - // (undocumented) - history: SearchRequest[]; - onRequestStart(handler: (searchSource: ISearchSource, options?: FetchOptions) => Promise): void; - serialize(): { - searchSourceJSON: string; - references: SavedObjectReference[]; - }; - // (undocumented) - setField(field: K, value: SearchSourceFields[K]): this; - // (undocumented) - setFields(newFields: SearchSourceFields): this; - // Warning: (ae-forgotten-export) The symbol "SearchSourceOptions" needs to be exported by the entry point index.d.ts - setParent(parent?: ISearchSource, options?: SearchSourceOptions): this; - setPreferredSearchStrategyId(searchStrategyId: string): void; -} - // Warning: (ae-missing-release-tag) "SearchSourceFields" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -1876,21 +1818,21 @@ export type TSearchStrategyProvider = (context: ISearc // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "getRoutes" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:388:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:389:1 - (ae-forgotten-export) The symbol "convertDateRangeToString" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:391:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:401:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:402:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:406:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:407:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:410:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:411:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:414:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:381:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:381:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:381:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:381:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:386:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:387:1 - (ae-forgotten-export) The symbol "convertDateRangeToString" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:389:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:398:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:399:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:404:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:405:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:408:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:409:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:412:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:33:33 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:37:1 - (ae-forgotten-export) The symbol "QueryStateChange" needs to be exported by the entry point index.d.ts // src/plugins/data/public/types.ts:52:5 - (ae-forgotten-export) The symbol "createFiltersFromValueClickAction" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/data/public/search/aggs/agg_params.test.ts b/src/plugins/data/public/search/aggs/agg_params.test.ts index 784be803e2644..e116bdca157ff 100644 --- a/src/plugins/data/public/search/aggs/agg_params.test.ts +++ b/src/plugins/data/public/search/aggs/agg_params.test.ts @@ -25,13 +25,15 @@ import { AggParamType } from '../aggs/param_types/agg'; import { fieldFormatsServiceMock } from '../../field_formats/mocks'; import { notificationServiceMock } from '../../../../../../src/core/public/mocks'; import { AggTypeDependencies } from './agg_type'; +import { InternalStartServices } from '../../types'; describe('AggParams class', () => { const aggTypesDependencies: AggTypeDependencies = { - getInternalStartServices: () => ({ - fieldFormats: fieldFormatsServiceMock.createStartContract(), - notifications: notificationServiceMock.createStartContract(), - }), + getInternalStartServices: () => + (({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + } as unknown) as InternalStartServices), }; describe('constructor args', () => { diff --git a/src/plugins/data/public/search/aggs/agg_type.test.ts b/src/plugins/data/public/search/aggs/agg_type.test.ts index 0c9e110c34ae6..369ae0ce0b3a5 100644 --- a/src/plugins/data/public/search/aggs/agg_type.test.ts +++ b/src/plugins/data/public/search/aggs/agg_type.test.ts @@ -21,19 +21,21 @@ import { AggType, AggTypeConfig, AggTypeDependencies } from './agg_type'; import { IAggConfig } from './agg_config'; import { fieldFormatsServiceMock } from '../../field_formats/mocks'; import { notificationServiceMock } from '../../../../../../src/core/public/mocks'; +import { InternalStartServices } from '../../types'; describe('AggType Class', () => { let dependencies: AggTypeDependencies; beforeEach(() => { dependencies = { - getInternalStartServices: () => ({ - fieldFormats: { - ...fieldFormatsServiceMock.createStartContract(), - getDefaultInstance: jest.fn(() => 'default') as any, - }, - notifications: notificationServiceMock.createStartContract(), - }), + getInternalStartServices: () => + (({ + fieldFormats: { + ...fieldFormatsServiceMock.createStartContract(), + getDefaultInstance: jest.fn(() => 'default') as any, + }, + notifications: notificationServiceMock.createStartContract(), + } as unknown) as InternalStartServices), }; }); diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/date_histogram.test.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/date_histogram.test.ts index 7778fcb36bcd6..bb73c8a39df19 100644 --- a/src/plugins/data/public/search/aggs/buckets/create_filter/date_histogram.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/create_filter/date_histogram.test.ts @@ -32,6 +32,7 @@ import { RangeFilter } from '../../../../../common'; import { coreMock, notificationServiceMock } from '../../../../../../../core/public/mocks'; import { queryServiceMock } from '../../../../query/mocks'; import { fieldFormatsServiceMock } from '../../../../field_formats/mocks'; +import { InternalStartServices } from '../../../../types'; describe('AggConfig Filters', () => { describe('date_histogram', () => { @@ -47,10 +48,11 @@ describe('AggConfig Filters', () => { aggTypesDependencies = { uiSettings, query: queryServiceMock.createSetupContract(), - getInternalStartServices: () => ({ - fieldFormats: fieldFormatsServiceMock.createStartContract(), - notifications: notificationServiceMock.createStartContract(), - }), + getInternalStartServices: () => + (({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + } as unknown) as InternalStartServices), }; mockDataServices(); diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts index 4207fa92736f8..0d66d9cfcdca2 100644 --- a/src/plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts @@ -28,6 +28,7 @@ import { BUCKET_TYPES } from '../bucket_agg_types'; import { IBucketAggConfig } from '../bucket_agg_type'; import { coreMock, notificationServiceMock } from '../../../../../../../core/public/mocks'; import { fieldFormatsServiceMock } from '../../../../field_formats/mocks'; +import { InternalStartServices } from '../../../../types'; describe('AggConfig Filters', () => { describe('Date range', () => { @@ -38,10 +39,11 @@ describe('AggConfig Filters', () => { aggTypesDependencies = { uiSettings, - getInternalStartServices: () => ({ - fieldFormats: fieldFormatsServiceMock.createStartContract(), - notifications: notificationServiceMock.createStartContract(), - }), + getInternalStartServices: () => + (({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + } as unknown) as InternalStartServices), }; }); diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/filters.test.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/filters.test.ts index bf05f7463db6c..0fdb07cc4198a 100644 --- a/src/plugins/data/public/search/aggs/buckets/create_filter/filters.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/create_filter/filters.test.ts @@ -24,6 +24,7 @@ import { mockAggTypesRegistry } from '../../test_helpers'; import { IBucketAggConfig } from '../bucket_agg_type'; import { coreMock, notificationServiceMock } from '../../../../../../../core/public/mocks'; import { fieldFormatsServiceMock } from '../../../../field_formats/mocks'; +import { InternalStartServices } from '../../../../types'; describe('AggConfig Filters', () => { describe('filters', () => { @@ -34,10 +35,11 @@ describe('AggConfig Filters', () => { aggTypesDependencies = { uiSettings, - getInternalStartServices: () => ({ - fieldFormats: fieldFormatsServiceMock.createStartContract(), - notifications: notificationServiceMock.createStartContract(), - }), + getInternalStartServices: () => + (({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + } as unknown) as InternalStartServices), }; }); diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/ip_range.test.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/ip_range.test.ts index d85576a0ccb14..990adde5f8a0b 100644 --- a/src/plugins/data/public/search/aggs/buckets/create_filter/ip_range.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/create_filter/ip_range.test.ts @@ -26,16 +26,18 @@ import { BUCKET_TYPES } from '../bucket_agg_types'; import { IBucketAggConfig } from '../bucket_agg_type'; import { fieldFormatsServiceMock } from '../../../../field_formats/mocks'; import { notificationServiceMock } from '../../../../../../../core/public/mocks'; +import { InternalStartServices } from '../../../../types'; describe('AggConfig Filters', () => { describe('IP range', () => { const fieldFormats = fieldFormatsServiceMock.createStartContract(); const typesRegistry = mockAggTypesRegistry([ getIpRangeBucketAgg({ - getInternalStartServices: () => ({ - fieldFormats, - notifications: notificationServiceMock.createStartContract(), - }), + getInternalStartServices: () => + (({ + fieldFormats, + notifications: notificationServiceMock.createStartContract(), + } as unknown) as InternalStartServices), }), ]); const getAggConfigs = (aggs: CreateAggConfigParams[]) => { diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/range.test.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/range.test.ts index cadd8e9fe13ed..564e7b4763c8d 100644 --- a/src/plugins/data/public/search/aggs/buckets/create_filter/range.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/create_filter/range.test.ts @@ -26,6 +26,7 @@ import { BUCKET_TYPES } from '../bucket_agg_types'; import { IBucketAggConfig } from '../bucket_agg_type'; import { fieldFormatsServiceMock } from '../../../../field_formats/mocks'; import { notificationServiceMock } from '../../../../../../../core/public/mocks'; +import { InternalStartServices } from '../../../../types'; describe('AggConfig Filters', () => { describe('range', () => { @@ -33,10 +34,11 @@ describe('AggConfig Filters', () => { beforeEach(() => { aggTypesDependencies = { - getInternalStartServices: () => ({ - fieldFormats: fieldFormatsServiceMock.createStartContract(), - notifications: notificationServiceMock.createStartContract(), - }), + getInternalStartServices: () => + (({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + } as unknown) as InternalStartServices), }; mockDataServices(); diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/terms.test.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/terms.test.ts index d9ff63613b640..36e4bef025ef9 100644 --- a/src/plugins/data/public/search/aggs/buckets/create_filter/terms.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/create_filter/terms.test.ts @@ -27,6 +27,7 @@ import { Filter, ExistsFilter } from '../../../../../common'; import { RangeBucketAggDependencies } from '../range'; import { fieldFormatsServiceMock } from '../../../../field_formats/mocks'; import { notificationServiceMock } from '../../../../../../../core/public/mocks'; +import { InternalStartServices } from '../../../../types'; describe('AggConfig Filters', () => { describe('terms', () => { @@ -34,10 +35,11 @@ describe('AggConfig Filters', () => { beforeEach(() => { aggTypesDependencies = { - getInternalStartServices: () => ({ - fieldFormats: fieldFormatsServiceMock.createStartContract(), - notifications: notificationServiceMock.createStartContract(), - }), + getInternalStartServices: () => + (({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + } as unknown) as InternalStartServices), }; }); diff --git a/src/plugins/data/public/search/aggs/buckets/date_range.test.ts b/src/plugins/data/public/search/aggs/buckets/date_range.test.ts index f78f0cce732e7..e1881c3bbc7f4 100644 --- a/src/plugins/data/public/search/aggs/buckets/date_range.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/date_range.test.ts @@ -23,6 +23,7 @@ import { AggConfigs } from '../agg_configs'; import { mockAggTypesRegistry } from '../test_helpers'; import { BUCKET_TYPES } from './bucket_agg_types'; import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; +import { InternalStartServices } from '../../../types'; describe('date_range params', () => { let aggTypesDependencies: DateRangeBucketAggDependencies; @@ -32,10 +33,11 @@ describe('date_range params', () => { aggTypesDependencies = { uiSettings, - getInternalStartServices: () => ({ - fieldFormats: fieldFormatsServiceMock.createStartContract(), - notifications: notificationServiceMock.createStartContract(), - }), + getInternalStartServices: () => + (({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + } as unknown) as InternalStartServices), }; }); diff --git a/src/plugins/data/public/search/aggs/buckets/geo_hash.test.ts b/src/plugins/data/public/search/aggs/buckets/geo_hash.test.ts index 226faefe43482..877a817984dc6 100644 --- a/src/plugins/data/public/search/aggs/buckets/geo_hash.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/geo_hash.test.ts @@ -24,6 +24,7 @@ import { BUCKET_TYPES } from './bucket_agg_types'; import { notificationServiceMock } from '../../../../../../../src/core/public/mocks'; import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; import { BucketAggType, IBucketAggConfig } from './bucket_agg_type'; +import { InternalStartServices } from '../../../types'; describe('Geohash Agg', () => { let aggTypesDependencies: GeoHashBucketAggDependencies; @@ -31,10 +32,11 @@ describe('Geohash Agg', () => { beforeEach(() => { aggTypesDependencies = { - getInternalStartServices: () => ({ - fieldFormats: fieldFormatsServiceMock.createStartContract(), - notifications: notificationServiceMock.createStartContract(), - }), + getInternalStartServices: () => + (({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + } as unknown) as InternalStartServices), }; geoHashBucketAgg = getGeoHashBucketAgg(aggTypesDependencies); diff --git a/src/plugins/data/public/search/aggs/buckets/histogram.test.ts b/src/plugins/data/public/search/aggs/buckets/histogram.test.ts index a55c32951232a..4756669f5b4b3 100644 --- a/src/plugins/data/public/search/aggs/buckets/histogram.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/histogram.test.ts @@ -29,6 +29,7 @@ import { } from './histogram'; import { BucketAggType } from './bucket_agg_type'; import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; +import { InternalStartServices } from '../../../types'; describe('Histogram Agg', () => { let aggTypesDependencies: HistogramBucketAggDependencies; @@ -38,10 +39,11 @@ describe('Histogram Agg', () => { aggTypesDependencies = { uiSettings, - getInternalStartServices: () => ({ - fieldFormats: fieldFormatsServiceMock.createStartContract(), - notifications: notificationServiceMock.createStartContract(), - }), + getInternalStartServices: () => + (({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + } as unknown) as InternalStartServices), }; }); diff --git a/src/plugins/data/public/search/aggs/buckets/range.test.ts b/src/plugins/data/public/search/aggs/buckets/range.test.ts index 144d2b779e950..4c2d3af1ab734 100644 --- a/src/plugins/data/public/search/aggs/buckets/range.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/range.test.ts @@ -24,6 +24,7 @@ import { BUCKET_TYPES } from './bucket_agg_types'; import { FieldFormatsGetConfigFn, NumberFormat } from '../../../../common'; import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; import { notificationServiceMock } from '../../../../../../../src/core/public/mocks'; +import { InternalStartServices } from '../../../types'; const buckets = [ { @@ -50,10 +51,11 @@ describe('Range Agg', () => { beforeEach(() => { aggTypesDependencies = { - getInternalStartServices: () => ({ - fieldFormats: fieldFormatsServiceMock.createStartContract(), - notifications: notificationServiceMock.createStartContract(), - }), + getInternalStartServices: () => + (({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + } as unknown) as InternalStartServices), }; mockDataServices(); diff --git a/src/plugins/data/public/search/aggs/buckets/significant_terms.test.ts b/src/plugins/data/public/search/aggs/buckets/significant_terms.test.ts index d0ace5a50c28d..156f7f8108482 100644 --- a/src/plugins/data/public/search/aggs/buckets/significant_terms.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/significant_terms.test.ts @@ -26,6 +26,7 @@ import { } from './significant_terms'; import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; import { notificationServiceMock } from '../../../../../../../src/core/public/mocks'; +import { InternalStartServices } from '../../../types'; describe('Significant Terms Agg', () => { describe('order agg editor UI', () => { @@ -34,10 +35,11 @@ describe('Significant Terms Agg', () => { beforeEach(() => { aggTypesDependencies = { - getInternalStartServices: () => ({ - fieldFormats: fieldFormatsServiceMock.createStartContract(), - notifications: notificationServiceMock.createStartContract(), - }), + getInternalStartServices: () => + (({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + } as unknown) as InternalStartServices), }; }); diff --git a/src/plugins/data/public/search/aggs/index.test.ts b/src/plugins/data/public/search/aggs/index.test.ts index 419c3fdab1caf..382c5a10c2da5 100644 --- a/src/plugins/data/public/search/aggs/index.test.ts +++ b/src/plugins/data/public/search/aggs/index.test.ts @@ -24,6 +24,7 @@ import { isBucketAggType } from './buckets/bucket_agg_type'; import { isMetricAggType } from './metrics/metric_agg_type'; import { QueryStart } from '../../query'; import { FieldFormatsStart } from '../../field_formats'; +import { InternalStartServices } from '../../types'; describe('AggTypesComponent', () => { const coreSetup = coreMock.createSetup(); @@ -32,10 +33,11 @@ describe('AggTypesComponent', () => { const aggTypes = getAggTypes({ uiSettings: coreSetup.uiSettings, query: {} as QueryStart, - getInternalStartServices: () => ({ - notifications: coreStart.notifications, - fieldFormats: {} as FieldFormatsStart, - }), + getInternalStartServices: () => + (({ + notifications: coreStart.notifications, + fieldFormats: {} as FieldFormatsStart, + } as unknown) as InternalStartServices), }); const { buckets, metrics } = aggTypes; diff --git a/src/plugins/data/public/search/aggs/metrics/median.test.ts b/src/plugins/data/public/search/aggs/metrics/median.test.ts index de3ca646ead9e..71c48f04a3ca8 100644 --- a/src/plugins/data/public/search/aggs/metrics/median.test.ts +++ b/src/plugins/data/public/search/aggs/metrics/median.test.ts @@ -23,14 +23,16 @@ import { mockAggTypesRegistry } from '../test_helpers'; import { METRIC_TYPES } from './metric_agg_types'; import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; import { notificationServiceMock } from '../../../../../../../src/core/public/mocks'; +import { InternalStartServices } from '../../../types'; describe('AggTypeMetricMedianProvider class', () => { let aggConfigs: IAggConfigs; const aggTypesDependencies: MedianMetricAggDependencies = { - getInternalStartServices: () => ({ - fieldFormats: fieldFormatsServiceMock.createStartContract(), - notifications: notificationServiceMock.createStartContract(), - }), + getInternalStartServices: () => + (({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + } as unknown) as InternalStartServices), }; beforeEach(() => { diff --git a/src/plugins/data/public/search/aggs/metrics/parent_pipeline.test.ts b/src/plugins/data/public/search/aggs/metrics/parent_pipeline.test.ts index 3beb92a2fa000..f386034ea8a7b 100644 --- a/src/plugins/data/public/search/aggs/metrics/parent_pipeline.test.ts +++ b/src/plugins/data/public/search/aggs/metrics/parent_pipeline.test.ts @@ -25,14 +25,15 @@ import { AggConfigs } from '../agg_configs'; import { mockAggTypesRegistry } from '../test_helpers'; import { IMetricAggConfig, MetricAggType } from './metric_agg_type'; import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; -import { GetInternalStartServicesFn } from '../../../types'; +import { GetInternalStartServicesFn, InternalStartServices } from '../../../types'; import { notificationServiceMock } from '../../../../../../../src/core/public/mocks'; describe('parent pipeline aggs', function() { - const getInternalStartServices: GetInternalStartServicesFn = () => ({ - fieldFormats: fieldFormatsServiceMock.createStartContract(), - notifications: notificationServiceMock.createStartContract(), - }); + const getInternalStartServices: GetInternalStartServicesFn = () => + (({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + } as unknown) as InternalStartServices); const typesRegistry = mockAggTypesRegistry(); diff --git a/src/plugins/data/public/search/aggs/metrics/percentile_ranks.test.ts b/src/plugins/data/public/search/aggs/metrics/percentile_ranks.test.ts index 1b94ecd602075..d20530a17ca65 100644 --- a/src/plugins/data/public/search/aggs/metrics/percentile_ranks.test.ts +++ b/src/plugins/data/public/search/aggs/metrics/percentile_ranks.test.ts @@ -27,14 +27,16 @@ import { mockAggTypesRegistry } from '../test_helpers'; import { METRIC_TYPES } from './metric_agg_types'; import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; import { notificationServiceMock } from '../../../../../../../src/core/public/mocks'; +import { InternalStartServices } from '../../../types'; describe('AggTypesMetricsPercentileRanksProvider class', function() { let aggConfigs: IAggConfigs; const aggTypesDependencies: PercentileRanksMetricAggDependencies = { - getInternalStartServices: () => ({ - fieldFormats: fieldFormatsServiceMock.createStartContract(), - notifications: notificationServiceMock.createStartContract(), - }), + getInternalStartServices: () => + (({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + } as unknown) as InternalStartServices), }; beforeEach(() => { diff --git a/src/plugins/data/public/search/aggs/metrics/percentiles.test.ts b/src/plugins/data/public/search/aggs/metrics/percentiles.test.ts index 76da2fe3eb62c..0ac1e8417514c 100644 --- a/src/plugins/data/public/search/aggs/metrics/percentiles.test.ts +++ b/src/plugins/data/public/search/aggs/metrics/percentiles.test.ts @@ -27,14 +27,16 @@ import { mockAggTypesRegistry } from '../test_helpers'; import { METRIC_TYPES } from './metric_agg_types'; import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; import { notificationServiceMock } from '../../../../../../../src/core/public/mocks'; +import { InternalStartServices } from '../../../types'; describe('AggTypesMetricsPercentilesProvider class', () => { let aggConfigs: IAggConfigs; const aggTypesDependencies: PercentilesMetricAggDependencies = { - getInternalStartServices: () => ({ - fieldFormats: fieldFormatsServiceMock.createStartContract(), - notifications: notificationServiceMock.createStartContract(), - }), + getInternalStartServices: () => + (({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + } as unknown) as InternalStartServices), }; beforeEach(() => { diff --git a/src/plugins/data/public/search/aggs/metrics/sibling_pipeline.test.ts b/src/plugins/data/public/search/aggs/metrics/sibling_pipeline.test.ts index a47aa2c677ade..5e1834d3b4935 100644 --- a/src/plugins/data/public/search/aggs/metrics/sibling_pipeline.test.ts +++ b/src/plugins/data/public/search/aggs/metrics/sibling_pipeline.test.ts @@ -26,14 +26,15 @@ import { AggConfigs } from '../agg_configs'; import { IMetricAggConfig, MetricAggType } from './metric_agg_type'; import { mockAggTypesRegistry } from '../test_helpers'; import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; -import { GetInternalStartServicesFn } from '../../../types'; +import { GetInternalStartServicesFn, InternalStartServices } from '../../../types'; import { notificationServiceMock } from '../../../../../../../src/core/public/mocks'; describe('sibling pipeline aggs', () => { - const getInternalStartServices: GetInternalStartServicesFn = () => ({ - fieldFormats: fieldFormatsServiceMock.createStartContract(), - notifications: notificationServiceMock.createStartContract(), - }); + const getInternalStartServices: GetInternalStartServicesFn = () => + (({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + } as unknown) as InternalStartServices); const typesRegistry = mockAggTypesRegistry(); diff --git a/src/plugins/data/public/search/aggs/metrics/std_deviation.test.ts b/src/plugins/data/public/search/aggs/metrics/std_deviation.test.ts index d2370e1fed02c..536764b2bcf0b 100644 --- a/src/plugins/data/public/search/aggs/metrics/std_deviation.test.ts +++ b/src/plugins/data/public/search/aggs/metrics/std_deviation.test.ts @@ -27,13 +27,15 @@ import { mockAggTypesRegistry } from '../test_helpers'; import { METRIC_TYPES } from './metric_agg_types'; import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; import { notificationServiceMock } from '../../../../../../../src/core/public/mocks'; +import { InternalStartServices } from '../../../types'; describe('AggTypeMetricStandardDeviationProvider class', () => { const aggTypesDependencies: StdDeviationMetricAggDependencies = { - getInternalStartServices: () => ({ - fieldFormats: fieldFormatsServiceMock.createStartContract(), - notifications: notificationServiceMock.createStartContract(), - }), + getInternalStartServices: () => + (({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + } as unknown) as InternalStartServices), }; const typesRegistry = mockAggTypesRegistry([getStdDeviationMetricAgg(aggTypesDependencies)]); const getAggConfigs = (customLabel?: string) => { diff --git a/src/plugins/data/public/search/aggs/metrics/top_hit.test.ts b/src/plugins/data/public/search/aggs/metrics/top_hit.test.ts index 142b8e4c83301..617e458cf6243 100644 --- a/src/plugins/data/public/search/aggs/metrics/top_hit.test.ts +++ b/src/plugins/data/public/search/aggs/metrics/top_hit.test.ts @@ -25,15 +25,17 @@ import { IMetricAggConfig } from './metric_agg_type'; import { KBN_FIELD_TYPES } from '../../../../common'; import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; import { notificationServiceMock } from '../../../../../../../src/core/public/mocks'; +import { InternalStartServices } from '../../../types'; describe('Top hit metric', () => { let aggDsl: Record; let aggConfig: IMetricAggConfig; const aggTypesDependencies: TopHitMetricAggDependencies = { - getInternalStartServices: () => ({ - fieldFormats: fieldFormatsServiceMock.createStartContract(), - notifications: notificationServiceMock.createStartContract(), - }), + getInternalStartServices: () => + (({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + } as unknown) as InternalStartServices), }; const init = ({ diff --git a/src/plugins/data/public/search/aggs/param_types/field.test.ts b/src/plugins/data/public/search/aggs/param_types/field.test.ts index ea7931130b84a..2c51d9709f906 100644 --- a/src/plugins/data/public/search/aggs/param_types/field.test.ts +++ b/src/plugins/data/public/search/aggs/param_types/field.test.ts @@ -23,13 +23,15 @@ import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '../../../../common'; import { IAggConfig } from '../agg_config'; import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; import { notificationServiceMock } from '../../../../../../../src/core/public/mocks'; +import { InternalStartServices } from '../../../types'; describe('Field', () => { const fieldParamTypeDependencies: FieldParamTypeDependencies = { - getInternalStartServices: () => ({ - fieldFormats: fieldFormatsServiceMock.createStartContract(), - notifications: notificationServiceMock.createStartContract(), - }), + getInternalStartServices: () => + (({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + } as unknown) as InternalStartServices), }; const indexPattern = { diff --git a/src/plugins/data/public/search/aggs/test_helpers/mock_agg_types_registry.ts b/src/plugins/data/public/search/aggs/test_helpers/mock_agg_types_registry.ts index 2383affa2a8c5..3ff2fbf35ad7e 100644 --- a/src/plugins/data/public/search/aggs/test_helpers/mock_agg_types_registry.ts +++ b/src/plugins/data/public/search/aggs/test_helpers/mock_agg_types_registry.ts @@ -17,7 +17,6 @@ * under the License. */ -// eslint-disable-next-line @kbn/eslint/no-restricted-paths import { coreMock, notificationServiceMock } from '../../../../../../../src/core/public/mocks'; import { AggTypesRegistry, AggTypesRegistryStart } from '../agg_types_registry'; import { getAggTypes } from '../agg_types'; @@ -25,6 +24,7 @@ import { BucketAggType } from '../buckets/bucket_agg_type'; import { MetricAggType } from '../metrics/metric_agg_type'; import { queryServiceMock } from '../../../query/mocks'; import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; +import { InternalStartServices } from '../../../types'; /** * Testing utility which creates a new instance of AggTypesRegistry, @@ -53,14 +53,19 @@ export function mockAggTypesRegistry | MetricAggTyp } }); } else { - const core = coreMock.createSetup(); + const coreSetup = coreMock.createSetup(); + const coreStart = coreMock.createStart(); + const aggTypes = getAggTypes({ - uiSettings: core.uiSettings, + uiSettings: coreSetup.uiSettings, query: queryServiceMock.createSetupContract(), - getInternalStartServices: () => ({ - fieldFormats: fieldFormatsServiceMock.createStartContract(), - notifications: notificationServiceMock.createStartContract(), - }), + getInternalStartServices: () => + (({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + uiSettings: coreStart.uiSettings, + injectedMetadata: coreStart.injectedMetadata, + } as unknown) as InternalStartServices), }); aggTypes.buckets.forEach(type => registrySetup.registerBucket(type)); diff --git a/src/plugins/data/public/search/expressions/esaggs.ts b/src/plugins/data/public/search/expressions/esaggs.ts index 2341f4fe447db..087b83127079f 100644 --- a/src/plugins/data/public/search/expressions/esaggs.ts +++ b/src/plugins/data/public/search/expressions/esaggs.ts @@ -30,7 +30,7 @@ import { PersistedState } from '../../../../../plugins/visualizations/public'; import { Adapters } from '../../../../../plugins/inspector/public'; import { IAggConfigs } from '../aggs'; -import { ISearchSource, SearchSource } from '../search_source'; +import { ISearchSource } from '../search_source'; import { tabifyAggResponse } from '../tabify'; import { Filter, Query, serializeFieldFormat, TimeRange } from '../../../common'; import { FilterManager, getTime } from '../../query'; @@ -253,7 +253,8 @@ export const esaggs = (): ExpressionFunctionDefinition { test('Passes the additional arguments it is given to the search strategy', () => { const searchRequests = [{ _searchStrategyId: 0 }]; - const args = { searchService: {}, config: {}, esShardTimeout: 0 } as FetchHandlers; + const args = { legacySearchService: {}, config: {}, esShardTimeout: 0 } as FetchHandlers; callClient(searchRequests, [], args); diff --git a/src/plugins/data/public/search/legacy/default_search_strategy.test.ts b/src/plugins/data/public/search/legacy/default_search_strategy.test.ts index 835b02b3cd5c7..9e3d65a69bf02 100644 --- a/src/plugins/data/public/search/legacy/default_search_strategy.test.ts +++ b/src/plugins/data/public/search/legacy/default_search_strategy.test.ts @@ -62,10 +62,10 @@ describe('defaultSearchStrategy', function() { }, ], esShardTimeout: 0, - searchService, + legacySearchService: searchService.__LEGACY, }; - es = searchArgs.searchService.__LEGACY.esClient; + es = searchArgs.legacySearchService.esClient; }); test('does not send max_concurrent_shard_requests by default', async () => { diff --git a/src/plugins/data/public/search/legacy/default_search_strategy.ts b/src/plugins/data/public/search/legacy/default_search_strategy.ts index 1552410f9090c..3216803dcbfa2 100644 --- a/src/plugins/data/public/search/legacy/default_search_strategy.ts +++ b/src/plugins/data/public/search/legacy/default_search_strategy.ts @@ -32,11 +32,11 @@ export const defaultSearchStrategy: SearchStrategyProvider = { function msearch({ searchRequests, - searchService, + legacySearchService, config, esShardTimeout, }: SearchStrategySearchParams) { - const es = searchService.__LEGACY.esClient; + const es = legacySearchService.esClient; const inlineRequests = searchRequests.map(({ index, body, search_type: searchType }) => { const inlineHeader = { index: index.title || index, diff --git a/src/plugins/data/public/search/mocks.ts b/src/plugins/data/public/search/mocks.ts index cb1c625a72959..dd196074c8173 100644 --- a/src/plugins/data/public/search/mocks.ts +++ b/src/plugins/data/public/search/mocks.ts @@ -20,20 +20,19 @@ import { searchAggsSetupMock, searchAggsStartMock } from './aggs/mocks'; import { AggTypeFieldFilters } from './aggs/param_types/filter'; import { ISearchStart } from './types'; +import { searchSourceMock, createSearchSourceMock } from './search_source/mocks'; -export * from './search_source/mocks'; - -export const searchSetupMock = { +const searchSetupMock = { aggs: searchAggsSetupMock(), registerSearchStrategyContext: jest.fn(), registerSearchStrategyProvider: jest.fn(), }; -export const searchStartMock: jest.Mocked = { +const searchStartMock: jest.Mocked = { aggs: searchAggsStartMock(), setInterceptor: jest.fn(), search: jest.fn(), - createSearchSource: jest.fn(), + searchSource: searchSourceMock, __LEGACY: { AggConfig: jest.fn() as any, AggType: jest.fn(), @@ -48,3 +47,5 @@ export const searchStartMock: jest.Mocked = { }, }, }; + +export { searchSetupMock, searchStartMock, createSearchSourceMock }; diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts index 916278a96659b..3f3266b5fe90f 100644 --- a/src/plugins/data/public/search/search_service.ts +++ b/src/plugins/data/public/search/search_service.ts @@ -20,13 +20,18 @@ import { Plugin, CoreSetup, CoreStart, PackageInfo } from '../../../../core/public'; import { SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider } from './sync_search_strategy'; +import { + createSearchSourceFromJSON, + SearchSource, + SearchSourceDependencies, + SearchSourceFields, +} from './search_source'; import { ISearchSetup, ISearchStart, TSearchStrategyProvider, TSearchStrategiesMap } from './types'; import { TStrategyTypes } from './strategy_types'; import { getEsClient, LegacyApiCaller } from './legacy'; import { ES_SEARCH_STRATEGY, DEFAULT_SEARCH_STRATEGY } from '../../common/search'; import { esSearchStrategyProvider } from './es_search'; import { IndexPatternsContract } from '../index_patterns/index_patterns'; -import { createSearchSource } from './search_source'; import { QuerySetup } from '../query'; import { GetInternalStartServicesFn } from '../types'; import { SearchInterceptor } from './search_interceptor'; @@ -43,8 +48,8 @@ import { parentPipelineAggHelper, siblingPipelineAggHelper, } from './aggs'; - import { FieldFormatsStart } from '../field_formats'; +import { ISearchGeneric } from './i_search'; interface SearchServiceSetupDependencies { packageInfo: PackageInfo; @@ -52,9 +57,9 @@ interface SearchServiceSetupDependencies { getInternalStartServices: GetInternalStartServicesFn; } -interface SearchStartDependencies { - fieldFormats: FieldFormatsStart; +interface SearchServiceStartDependencies { indexPatterns: IndexPatternsContract; + fieldFormats: FieldFormatsStart; } /** @@ -117,10 +122,7 @@ export class SearchService implements Plugin { }; } - public start( - core: CoreStart, - { fieldFormats, indexPatterns }: SearchStartDependencies - ): ISearchStart { + public start(core: CoreStart, dependencies: SearchServiceStartDependencies): ISearchStart { /** * A global object that intercepts all searches and provides convenience methods for cancelling * all pending search requests, as well as getting the number of pending search requests. @@ -135,40 +137,54 @@ export class SearchService implements Plugin { const aggTypesStart = this.aggTypesRegistry.start(); + const search: ISearchGeneric = (request, options, strategyName) => { + const strategyProvider = this.getSearchStrategy(strategyName || DEFAULT_SEARCH_STRATEGY); + const searchStrategy = strategyProvider({ + core, + getSearchStrategy: this.getSearchStrategy, + }); + return this.searchInterceptor.search(searchStrategy.search as any, request, options); + }; + + const legacySearch = { + esClient: this.esClient!, + AggConfig, + AggType, + aggTypeFieldFilters, + FieldParamType, + MetricAggType, + parentPipelineAggHelper, + siblingPipelineAggHelper, + }; + + const searchSourceDependencies: SearchSourceDependencies = { + uiSettings: core.uiSettings, + injectedMetadata: core.injectedMetadata, + search, + legacySearch, + }; + return { aggs: { calculateAutoTimeExpression: getCalculateAutoTimeExpression(core.uiSettings), createAggConfigs: (indexPattern, configStates = [], schemas) => { return new AggConfigs(indexPattern, configStates, { + fieldFormats: dependencies.fieldFormats, typesRegistry: aggTypesStart, - fieldFormats, }); }, types: aggTypesStart, }, - search: (request, options, strategyName) => { - const strategyProvider = this.getSearchStrategy(strategyName || DEFAULT_SEARCH_STRATEGY); - const { search } = strategyProvider({ - core, - getSearchStrategy: this.getSearchStrategy, - }); - return this.searchInterceptor.search(search as any, request, options); + search, + searchSource: { + create: (fields?: SearchSourceFields) => new SearchSource(fields, searchSourceDependencies), + fromJSON: createSearchSourceFromJSON(dependencies.indexPatterns, searchSourceDependencies), }, setInterceptor: (searchInterceptor: SearchInterceptor) => { // TODO: should an intercepror have a destroy method? this.searchInterceptor = searchInterceptor; }, - createSearchSource: createSearchSource(indexPatterns), - __LEGACY: { - esClient: this.esClient!, - AggConfig, - AggType, - aggTypeFieldFilters, - FieldParamType, - MetricAggType, - parentPipelineAggHelper, - siblingPipelineAggHelper, - }, + __LEGACY: legacySearch, }; } diff --git a/src/plugins/data/public/search/search_source/create_search_source.test.ts b/src/plugins/data/public/search/search_source/create_search_source.test.ts index d49ce5a0d11f8..efa63b0722e28 100644 --- a/src/plugins/data/public/search/search_source/create_search_source.test.ts +++ b/src/plugins/data/public/search/search_source/create_search_source.test.ts @@ -16,30 +16,43 @@ * specific language governing permissions and limitations * under the License. */ -import { createSearchSource as createSearchSourceFactory } from './create_search_source'; +import { createSearchSourceFromJSON } from './create_search_source'; import { IIndexPattern } from '../../../common/index_patterns'; import { IndexPatternsContract } from '../../index_patterns/index_patterns'; import { Filter } from '../../../common/es_query/filters'; +import { coreMock } from '../../../../../core/public/mocks'; +import { dataPluginMock } from '../../mocks'; -describe('createSearchSource', function() { - let createSearchSource: ReturnType; +describe('createSearchSource', () => { const indexPatternMock: IIndexPattern = {} as IIndexPattern; let indexPatternContractMock: jest.Mocked; + let dependencies: any; + let createSearchSource: ReturnType; beforeEach(() => { + const core = coreMock.createStart(); + const data = dataPluginMock.createStartContract(); + + dependencies = { + searchService: data.search, + uiSettings: core.uiSettings, + injectedMetadata: core.injectedMetadata, + }; + indexPatternContractMock = ({ get: jest.fn().mockReturnValue(Promise.resolve(indexPatternMock)), } as unknown) as jest.Mocked; - createSearchSource = createSearchSourceFactory(indexPatternContractMock); + + createSearchSource = createSearchSourceFromJSON(indexPatternContractMock, dependencies); }); - it('should fail if JSON is invalid', () => { + test('should fail if JSON is invalid', () => { expect(createSearchSource('{', [])).rejects.toThrow(); expect(createSearchSource('0', [])).rejects.toThrow(); expect(createSearchSource('"abcdefg"', [])).rejects.toThrow(); }); - it('should set fields', async () => { + test('should set fields', async () => { const searchSource = await createSearchSource( JSON.stringify({ highlightAll: true, @@ -50,6 +63,7 @@ describe('createSearchSource', function() { }), [] ); + expect(searchSource.getOwnField('highlightAll')).toBe(true); expect(searchSource.getOwnField('query')).toEqual({ query: '', @@ -57,7 +71,7 @@ describe('createSearchSource', function() { }); }); - it('should resolve referenced index pattern', async () => { + test('should resolve referenced index pattern', async () => { const searchSource = await createSearchSource( JSON.stringify({ indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.index', @@ -70,11 +84,12 @@ describe('createSearchSource', function() { }, ] ); + expect(indexPatternContractMock.get).toHaveBeenCalledWith('123-456'); expect(searchSource.getOwnField('index')).toBe(indexPatternMock); }); - it('should set filters and resolve referenced index patterns', async () => { + test('should set filters and resolve referenced index patterns', async () => { const searchSource = await createSearchSource( JSON.stringify({ filter: [ @@ -110,6 +125,7 @@ describe('createSearchSource', function() { ] ); const filters = searchSource.getOwnField('filter') as Filter[]; + expect(filters[0]).toMatchInlineSnapshot(` Object { "$state": Object { @@ -135,7 +151,7 @@ describe('createSearchSource', function() { `); }); - it('should migrate legacy queries on the fly', async () => { + test('should migrate legacy queries on the fly', async () => { const searchSource = await createSearchSource( JSON.stringify({ highlightAll: true, @@ -143,6 +159,7 @@ describe('createSearchSource', function() { }), [] ); + expect(searchSource.getOwnField('query')).toEqual({ query: 'a:b', language: 'lucene', diff --git a/src/plugins/data/public/search/search_source/create_search_source.ts b/src/plugins/data/public/search/search_source/create_search_source.ts index 35b7ac4eb9762..cc98f433b3a03 100644 --- a/src/plugins/data/public/search/search_source/create_search_source.ts +++ b/src/plugins/data/public/search/search_source/create_search_source.ts @@ -16,11 +16,11 @@ * specific language governing permissions and limitations * under the License. */ -import _ from 'lodash'; +import { transform, defaults, isFunction } from 'lodash'; import { SavedObjectReference } from 'kibana/public'; import { migrateLegacyQuery } from '../../../../kibana_legacy/public'; import { InvalidJSONProperty } from '../../../../kibana_utils/public'; -import { SearchSource } from './search_source'; +import { SearchSourceDependencies, SearchSource, ISearchSource } from './search_source'; import { IndexPatternsContract } from '../../index_patterns/index_patterns'; import { SearchSourceFields } from './types'; @@ -38,12 +38,16 @@ import { SearchSourceFields } from './types'; * returned by `serializeSearchSource` and `references`, a list of references including the ones * returned by `serializeSearchSource`. * + * * @public */ -export const createSearchSource = (indexPatterns: IndexPatternsContract) => async ( +export const createSearchSourceFromJSON = ( + indexPatterns: IndexPatternsContract, + searchSourceDependencies: SearchSourceDependencies +) => async ( searchSourceJson: string, references: SavedObjectReference[] -) => { - const searchSource = new SearchSource(); +): Promise => { + const searchSource = new SearchSource({}, searchSourceDependencies); // if we have a searchSource, set its values based on the searchSourceJson field let searchSourceValues: Record; @@ -90,17 +94,17 @@ export const createSearchSource = (indexPatterns: IndexPatternsContract) => asyn } const searchSourceFields = searchSource.getFields(); - const fnProps = _.transform( + const fnProps = transform( searchSourceFields, function(dynamic, val, name) { - if (_.isFunction(val) && name) dynamic[name] = val; + if (isFunction(val) && name) dynamic[name] = val; }, {} ); // This assignment might hide problems because the type of values passed from the parsed JSON // might not fit the SearchSourceFields interface. - const newFields: SearchSourceFields = _.defaults(searchSourceValues, fnProps); + const newFields: SearchSourceFields = defaults(searchSourceValues, fnProps); searchSource.setFields(newFields); const query = searchSource.getOwnField('query'); diff --git a/src/plugins/data/public/search/search_source/index.ts b/src/plugins/data/public/search/search_source/index.ts index 0e9f530d0968a..9c4106b2dc616 100644 --- a/src/plugins/data/public/search/search_source/index.ts +++ b/src/plugins/data/public/search/search_source/index.ts @@ -17,6 +17,6 @@ * under the License. */ -export * from './search_source'; -export { createSearchSource } from './create_search_source'; +export { SearchSource, ISearchSource, SearchSourceDependencies } from './search_source'; +export { createSearchSourceFromJSON } from './create_search_source'; export { SortDirection, EsQuerySortValue, SearchSourceFields } from './types'; diff --git a/src/plugins/data/public/search/search_source/mocks.ts b/src/plugins/data/public/search/search_source/mocks.ts index 1ef7c1187a9e0..157331ea87bb0 100644 --- a/src/plugins/data/public/search/search_source/mocks.ts +++ b/src/plugins/data/public/search/search_source/mocks.ts @@ -17,9 +17,15 @@ * under the License. */ -import { ISearchSource } from './search_source'; +import { + injectedMetadataServiceMock, + uiSettingsServiceMock, +} from '../../../../../core/public/mocks'; -export const searchSourceMock: MockedKeys = { +import { ISearchSource, SearchSource } from './search_source'; +import { SearchSourceFields } from './types'; + +export const searchSourceInstanceMock: MockedKeys = { setPreferredSearchStrategyId: jest.fn(), setFields: jest.fn().mockReturnThis(), setField: jest.fn().mockReturnThis(), @@ -39,3 +45,21 @@ export const searchSourceMock: MockedKeys = { history: [], serialize: jest.fn(), }; + +export const searchSourceMock = { + create: jest.fn().mockReturnValue(searchSourceInstanceMock), + fromJSON: jest.fn().mockReturnValue(searchSourceInstanceMock), +}; + +export const createSearchSourceMock = (fields?: SearchSourceFields) => + new SearchSource(fields, { + search: jest.fn(), + legacySearch: { + esClient: { + search: jest.fn(), + msearch: jest.fn(), + }, + }, + uiSettings: uiSettingsServiceMock.createStartContract(), + injectedMetadata: injectedMetadataServiceMock.createStartContract(), + }); diff --git a/src/plugins/data/public/search/search_source/search_source.test.ts b/src/plugins/data/public/search/search_source/search_source.test.ts index 6e878844664ad..7783e65889a12 100644 --- a/src/plugins/data/public/search/search_source/search_source.test.ts +++ b/src/plugins/data/public/search/search_source/search_source.test.ts @@ -16,28 +16,13 @@ * specific language governing permissions and limitations * under the License. */ - +import { Observable } from 'rxjs'; import { SearchSource } from './search_source'; import { IndexPattern, SortDirection } from '../..'; -import { mockDataServices } from '../aggs/test_helpers'; -import { setSearchService } from '../../services'; -import { searchStartMock } from '../mocks'; import { fetchSoon } from '../legacy'; -import { CoreStart } from 'kibana/public'; -import { Observable } from 'rxjs'; - -// Setup search service mock -searchStartMock.search = jest.fn(() => { - return new Observable(subscriber => { - setTimeout(() => { - subscriber.next({ - rawResponse: '', - }); - subscriber.complete(); - }, 100); - }); -}) as any; -setSearchService(searchStartMock); +import { IUiSettingsClient } from '../../../../../core/public'; +import { dataPluginMock } from '../../../../data/public/mocks'; +import { coreMock } from '../../../../../core/public/mocks'; jest.mock('../legacy', () => ({ fetchSoon: jest.fn().mockResolvedValue({}), @@ -48,48 +33,70 @@ const getComputedFields = () => ({ scriptFields: [], docvalueFields: [], }); + const mockSource = { excludes: ['foo-*'] }; const mockSource2 = { excludes: ['bar-*'] }; + const indexPattern = ({ title: 'foo', getComputedFields, getSourceFiltering: () => mockSource, } as unknown) as IndexPattern; + const indexPattern2 = ({ title: 'foo', getComputedFields, getSourceFiltering: () => mockSource2, } as unknown) as IndexPattern; -describe('SearchSource', function() { - let uiSettingsMock: jest.Mocked; +describe('SearchSource', () => { + let mockSearchMethod: any; + let searchSourceDependencies: any; + beforeEach(() => { - const { core } = mockDataServices(); - uiSettingsMock = core.uiSettings; - jest.clearAllMocks(); + const core = coreMock.createStart(); + const data = dataPluginMock.createStartContract(); + + mockSearchMethod = jest.fn(() => { + return new Observable(subscriber => { + setTimeout(() => { + subscriber.next({ + rawResponse: '', + }); + subscriber.complete(); + }, 100); + }); + }); + + searchSourceDependencies = { + search: mockSearchMethod, + legacySearch: data.search.__LEGACY, + injectedMetadata: core.injectedMetadata, + uiSettings: core.uiSettings, + }; }); - describe('#setField()', function() { - it('sets the value for the property', function() { - const searchSource = new SearchSource(); + describe('#setField()', () => { + test('sets the value for the property', () => { + const searchSource = new SearchSource({}, searchSourceDependencies); searchSource.setField('aggs', 5); expect(searchSource.getField('aggs')).toBe(5); }); }); - describe('#getField()', function() { - it('gets the value for the property', function() { - const searchSource = new SearchSource(); + describe('#getField()', () => { + test('gets the value for the property', () => { + const searchSource = new SearchSource({}, searchSourceDependencies); searchSource.setField('aggs', 5); expect(searchSource.getField('aggs')).toBe(5); }); }); - describe(`#setField('index')`, function() { - describe('auto-sourceFiltering', function() { - describe('new index pattern assigned', function() { - it('generates a searchSource filter', async function() { - const searchSource = new SearchSource(); + describe(`#setField('index')`, () => { + describe('auto-sourceFiltering', () => { + describe('new index pattern assigned', () => { + test('generates a searchSource filter', async () => { + const searchSource = new SearchSource({}, searchSourceDependencies); expect(searchSource.getField('index')).toBe(undefined); expect(searchSource.getField('source')).toBe(undefined); searchSource.setField('index', indexPattern); @@ -98,8 +105,8 @@ describe('SearchSource', function() { expect(request._source).toBe(mockSource); }); - it('removes created searchSource filter on removal', async function() { - const searchSource = new SearchSource(); + test('removes created searchSource filter on removal', async () => { + const searchSource = new SearchSource({}, searchSourceDependencies); searchSource.setField('index', indexPattern); searchSource.setField('index', undefined); const request = await searchSource.getSearchRequestBody(); @@ -107,9 +114,9 @@ describe('SearchSource', function() { }); }); - describe('new index pattern assigned over another', function() { - it('replaces searchSource filter with new', async function() { - const searchSource = new SearchSource(); + describe('new index pattern assigned over another', () => { + test('replaces searchSource filter with new', async () => { + const searchSource = new SearchSource({}, searchSourceDependencies); searchSource.setField('index', indexPattern); searchSource.setField('index', indexPattern2); expect(searchSource.getField('index')).toBe(indexPattern2); @@ -117,8 +124,8 @@ describe('SearchSource', function() { expect(request._source).toBe(mockSource2); }); - it('removes created searchSource filter on removal', async function() { - const searchSource = new SearchSource(); + test('removes created searchSource filter on removal', async () => { + const searchSource = new SearchSource({}, searchSourceDependencies); searchSource.setField('index', indexPattern); searchSource.setField('index', indexPattern2); searchSource.setField('index', undefined); @@ -130,8 +137,8 @@ describe('SearchSource', function() { }); describe('#onRequestStart()', () => { - it('should be called when starting a request', async () => { - const searchSource = new SearchSource({ index: indexPattern }); + test('should be called when starting a request', async () => { + const searchSource = new SearchSource({ index: indexPattern }, searchSourceDependencies); const fn = jest.fn(); searchSource.onRequestStart(fn); const options = {}; @@ -139,9 +146,9 @@ describe('SearchSource', function() { expect(fn).toBeCalledWith(searchSource, options); }); - it('should not be called on parent searchSource', async () => { - const parent = new SearchSource(); - const searchSource = new SearchSource({ index: indexPattern }); + test('should not be called on parent searchSource', async () => { + const parent = new SearchSource({}, searchSourceDependencies); + const searchSource = new SearchSource({ index: indexPattern }, searchSourceDependencies); const fn = jest.fn(); searchSource.onRequestStart(fn); @@ -154,9 +161,12 @@ describe('SearchSource', function() { expect(parentFn).not.toBeCalled(); }); - it('should be called on parent searchSource if callParentStartHandlers is true', async () => { - const parent = new SearchSource(); - const searchSource = new SearchSource({ index: indexPattern }).setParent(parent, { + test('should be called on parent searchSource if callParentStartHandlers is true', async () => { + const parent = new SearchSource({}, searchSourceDependencies); + const searchSource = new SearchSource( + { index: indexPattern }, + searchSourceDependencies + ).setParent(parent, { callParentStartHandlers: true, }); @@ -174,19 +184,21 @@ describe('SearchSource', function() { describe('#legacy fetch()', () => { beforeEach(() => { - uiSettingsMock.get.mockImplementation(() => { - return true; // batchSearches = true - }); - }); + const core = coreMock.createStart(); - afterEach(() => { - uiSettingsMock.get.mockImplementation(() => { - return false; // batchSearches = false - }); + searchSourceDependencies = { + ...searchSourceDependencies, + uiSettings: { + ...core.uiSettings, + get: jest.fn(() => { + return true; // batchSearches = true + }), + } as IUiSettingsClient, + }; }); - it('should call msearch', async () => { - const searchSource = new SearchSource({ index: indexPattern }); + test('should call msearch', async () => { + const searchSource = new SearchSource({ index: indexPattern }, searchSourceDependencies); const options = {}; await searchSource.fetch(options); expect(fetchSoon).toBeCalledTimes(1); @@ -194,18 +206,19 @@ describe('SearchSource', function() { }); describe('#search service fetch()', () => { - it('should call msearch', async () => { - const searchSource = new SearchSource({ index: indexPattern }); + test('should call msearch', async () => { + const searchSource = new SearchSource({ index: indexPattern }, searchSourceDependencies); const options = {}; + await searchSource.fetch(options); - expect(searchStartMock.search).toBeCalledTimes(1); + expect(mockSearchMethod).toBeCalledTimes(1); }); }); - describe('#serialize', function() { - it('should reference index patterns', () => { + describe('#serialize', () => { + test('should reference index patterns', () => { const indexPattern123 = { id: '123' } as IndexPattern; - const searchSource = new SearchSource(); + const searchSource = new SearchSource({}, searchSourceDependencies); searchSource.setField('index', indexPattern123); const { searchSourceJSON, references } = searchSource.serialize(); expect(references[0].id).toEqual('123'); @@ -213,8 +226,8 @@ describe('SearchSource', function() { expect(JSON.parse(searchSourceJSON).indexRefName).toEqual(references[0].name); }); - it('should add other fields', () => { - const searchSource = new SearchSource(); + test('should add other fields', () => { + const searchSource = new SearchSource({}, searchSourceDependencies); searchSource.setField('highlightAll', true); searchSource.setField('from', 123456); const { searchSourceJSON } = searchSource.serialize(); @@ -222,8 +235,8 @@ describe('SearchSource', function() { expect(JSON.parse(searchSourceJSON).from).toEqual(123456); }); - it('should omit sort and size', () => { - const searchSource = new SearchSource(); + test('should omit sort and size', () => { + const searchSource = new SearchSource({}, searchSourceDependencies); searchSource.setField('highlightAll', true); searchSource.setField('from', 123456); searchSource.setField('sort', { field: SortDirection.asc }); @@ -232,8 +245,8 @@ describe('SearchSource', function() { expect(Object.keys(JSON.parse(searchSourceJSON))).toEqual(['highlightAll', 'from']); }); - it('should serialize filters', () => { - const searchSource = new SearchSource(); + test('should serialize filters', () => { + const searchSource = new SearchSource({}, searchSourceDependencies); const filter = [ { query: 'query', @@ -249,8 +262,8 @@ describe('SearchSource', function() { expect(JSON.parse(searchSourceJSON).filter).toEqual(filter); }); - it('should reference index patterns in filters separately from index field', () => { - const searchSource = new SearchSource(); + test('should reference index patterns in filters separately from index field', () => { + const searchSource = new SearchSource({}, searchSourceDependencies); const indexPattern123 = { id: '123' } as IndexPattern; searchSource.setField('index', indexPattern123); const filter = [ diff --git a/src/plugins/data/public/search/search_source/search_source.ts b/src/plugins/data/public/search/search_source/search_source.ts index 9d2bb889953cf..c9fecb3f7e9ce 100644 --- a/src/plugins/data/public/search/search_source/search_source.ts +++ b/src/plugins/data/public/search/search_source/search_source.ts @@ -69,34 +69,45 @@ * `appSearchSource`. */ -import _ from 'lodash'; +import { uniqueId, uniq, extend, pick, difference, set, omit, keys, isFunction } from 'lodash'; import { map } from 'rxjs/operators'; -import { SavedObjectReference } from 'kibana/public'; +import { CoreStart, SavedObjectReference } from 'kibana/public'; import { normalizeSortRequest } from './normalize_sort_request'; import { filterDocvalueFields } from './filter_docvalue_fields'; import { fieldWildcardFilter } from '../../../../kibana_utils/public'; -import { IIndexPattern, SearchRequest } from '../..'; +import { IIndexPattern, ISearchGeneric, SearchRequest } from '../..'; import { SearchSourceOptions, SearchSourceFields } from './types'; import { FetchOptions, RequestFailure, getSearchParams, handleResponse } from '../fetch'; -import { getSearchService, getUiSettings, getInjectedMetadata } from '../../services'; import { getEsQueryConfig, buildEsQuery, Filter } from '../../../common'; import { getHighlightRequest } from '../../../common/field_formats'; import { fetchSoon } from '../legacy'; +import { ISearchStartLegacy } from '../types'; -export type ISearchSource = Pick; +export interface SearchSourceDependencies { + uiSettings: CoreStart['uiSettings']; + search: ISearchGeneric; + legacySearch: ISearchStartLegacy; + injectedMetadata: CoreStart['injectedMetadata']; +} +/** @public **/ export class SearchSource { - private id: string = _.uniqueId('data_source'); + private id: string = uniqueId('data_source'); private searchStrategyId?: string; private parent?: SearchSource; private requestStartHandlers: Array< - (searchSource: ISearchSource, options?: FetchOptions) => Promise + (searchSource: SearchSource, options?: FetchOptions) => Promise > = []; private inheritOptions: SearchSourceOptions = {}; public history: SearchRequest[] = []; + private fields: SearchSourceFields; + private readonly dependencies: SearchSourceDependencies; - constructor(private fields: SearchSourceFields = {}) {} + constructor(fields: SearchSourceFields = {}, dependencies: SearchSourceDependencies) { + this.fields = fields; + this.dependencies = dependencies; + } /** *** * PUBLIC API @@ -147,11 +158,11 @@ export class SearchSource { } create() { - return new SearchSource(); + return new SearchSource({}, this.dependencies); } createCopy() { - const newSearchSource = new SearchSource(); + const newSearchSource = new SearchSource({}, this.dependencies); newSearchSource.setFields({ ...this.fields }); // when serializing the internal fields we lose the internal classes used in the index // pattern, so we have to set it again to workaround this behavior @@ -161,7 +172,7 @@ export class SearchSource { } createChild(options = {}) { - const childSearchSource = new SearchSource(); + const childSearchSource = new SearchSource({}, this.dependencies); childSearchSource.setParent(this, options); return childSearchSource; } @@ -191,16 +202,17 @@ export class SearchSource { * @return {Observable>} */ private fetch$(searchRequest: SearchRequest, signal?: AbortSignal) { - const esShardTimeout = getInjectedMetadata().getInjectedVar('esShardTimeout') as number; - const searchParams = getSearchParams(getUiSettings(), esShardTimeout); + const { search, injectedMetadata, uiSettings } = this.dependencies; + const esShardTimeout = injectedMetadata.getInjectedVar('esShardTimeout') as number; + const searchParams = getSearchParams(uiSettings, esShardTimeout); const params = { index: searchRequest.index.title || searchRequest.index, body: searchRequest.body, ...searchParams, }; - return getSearchService() - .search({ params, indexType: searchRequest.indexType }, { signal }) - .pipe(map(({ rawResponse }) => handleResponse(searchRequest, rawResponse))); + return search({ params, indexType: searchRequest.indexType }, { signal }).pipe( + map(({ rawResponse }) => handleResponse(searchRequest, rawResponse)) + ); } /** @@ -208,7 +220,9 @@ export class SearchSource { * @return {Promise>} */ private async legacyFetch(searchRequest: SearchRequest, options: FetchOptions) { - const esShardTimeout = getInjectedMetadata().getInjectedVar('esShardTimeout') as number; + const { injectedMetadata, legacySearch, uiSettings } = this.dependencies; + const esShardTimeout = injectedMetadata.getInjectedVar('esShardTimeout') as number; + return await fetchSoon( searchRequest, { @@ -216,8 +230,8 @@ export class SearchSource { ...options, }, { - searchService: getSearchService(), - config: getUiSettings(), + legacySearchService: legacySearch, + config: uiSettings, esShardTimeout, } ); @@ -228,13 +242,14 @@ export class SearchSource { * @async */ async fetch(options: FetchOptions = {}) { + const { uiSettings } = this.dependencies; await this.requestIsStarting(options); const searchRequest = await this.flatten(); this.history = [searchRequest]; let response; - if (getUiSettings().get('courier:batchSearches')) { + if (uiSettings.get('courier:batchSearches')) { response = await this.legacyFetch(searchRequest, options); } else { response = this.fetch$(searchRequest, options.abortSignal).toPromise(); @@ -253,7 +268,7 @@ export class SearchSource { * @return {undefined} */ onRequestStart( - handler: (searchSource: ISearchSource, options?: FetchOptions) => Promise + handler: (searchSource: SearchSource, options?: FetchOptions) => Promise ) { this.requestStartHandlers.push(handler); } @@ -326,13 +341,15 @@ export class SearchSource { } }; + const { uiSettings } = this.dependencies; + switch (key) { case 'filter': return addToRoot('filters', (data.filters || []).concat(val)); case 'query': return addToRoot(key, (data[key] || []).concat(val)); case 'fields': - const fields = _.uniq((data[key] || []).concat(val)); + const fields = uniq((data[key] || []).concat(val)); return addToRoot(key, fields); case 'index': case 'type': @@ -346,7 +363,7 @@ export class SearchSource { const sort = normalizeSortRequest( val, this.getField('index'), - getUiSettings().get('sort:options') + uiSettings.get('sort:options') ); return addToBody(key, sort); default: @@ -389,7 +406,7 @@ export class SearchSource { body.stored_fields = computedFields.storedFields; body.script_fields = body.script_fields || {}; - _.extend(body.script_fields, computedFields.scriptFields); + extend(body.script_fields, computedFields.scriptFields); const defaultDocValueFields = computedFields.docvalueFields ? computedFields.docvalueFields @@ -400,9 +417,11 @@ export class SearchSource { body._source = index.getSourceFiltering(); } + const { uiSettings } = this.dependencies; + if (body._source) { // exclude source fields for this index pattern specified by the user - const filter = fieldWildcardFilter(body._source.excludes, getUiSettings().get('metaFields')); + const filter = fieldWildcardFilter(body._source.excludes, uiSettings.get('metaFields')); body.docvalue_fields = body.docvalue_fields.filter((docvalueField: any) => filter(docvalueField.field) ); @@ -412,19 +431,19 @@ export class SearchSource { if (fields) { // filter out the docvalue_fields, and script_fields to only include those that we are concerned with body.docvalue_fields = filterDocvalueFields(body.docvalue_fields, fields); - body.script_fields = _.pick(body.script_fields, fields); + body.script_fields = pick(body.script_fields, fields); // request the remaining fields from both stored_fields and _source - const remainingFields = _.difference(fields, _.keys(body.script_fields)); + const remainingFields = difference(fields, keys(body.script_fields)); body.stored_fields = remainingFields; - _.set(body, '_source.includes', remainingFields); + set(body, '_source.includes', remainingFields); } - const esQueryConfigs = getEsQueryConfig(getUiSettings()); + const esQueryConfigs = getEsQueryConfig(uiSettings); body.query = buildEsQuery(index, query, filters, esQueryConfigs); if (highlightAll && body.query) { - body.highlight = getHighlightRequest(body.query, getUiSettings().get('doc_table:highlight')); + body.highlight = getHighlightRequest(body.query, uiSettings.get('doc_table:highlight')); delete searchRequest.highlightAll; } @@ -467,7 +486,7 @@ export class SearchSource { const { filter: originalFilters, ...searchSourceFields - }: Omit = _.omit(this.getFields(), ['sort', 'size']); + }: Omit = omit(this.getFields(), ['sort', 'size']); let serializedSearchSourceFields: Omit & { indexRefName?: string; filter?: Array & { meta: Filter['meta'] & { indexRefName?: string } }>; @@ -524,10 +543,13 @@ export class SearchSource { return filterField; } - if (_.isFunction(filterField)) { + if (isFunction(filterField)) { return this.getFilters(filterField()); } return [filterField]; } } + +/** @public **/ +export type ISearchSource = Pick; diff --git a/src/plugins/data/public/search/types.ts b/src/plugins/data/public/search/types.ts index 2122e4e82ec1d..99d111ce1574e 100644 --- a/src/plugins/data/public/search/types.ts +++ b/src/plugins/data/public/search/types.ts @@ -17,13 +17,13 @@ * under the License. */ -import { CoreStart } from 'kibana/public'; -import { createSearchSource } from './search_source'; +import { CoreStart, SavedObjectReference } from 'kibana/public'; import { SearchAggsSetup, SearchAggsStart, SearchAggsStartLegacy } from './aggs'; import { ISearch, ISearchGeneric } from './i_search'; import { TStrategyTypes } from './strategy_types'; import { LegacyApiCaller } from './legacy/es_client'; import { SearchInterceptor } from './search_interceptor'; +import { ISearchSource, SearchSourceFields } from './search_source'; export interface ISearchContext { core: CoreStart; @@ -60,7 +60,7 @@ export type TRegisterSearchStrategyProvider = ( searchStrategyProvider: TSearchStrategyProvider ) => void; -interface ISearchStartLegacy { +export interface ISearchStartLegacy { esClient: LegacyApiCaller; } @@ -81,6 +81,12 @@ export interface ISearchStart { aggs: SearchAggsStart; setInterceptor: (searchInterceptor: SearchInterceptor) => void; search: ISearchGeneric; - createSearchSource: ReturnType; + searchSource: { + create: (fields?: SearchSourceFields) => ISearchSource; + fromJSON: ( + searchSourceJson: string, + references: SavedObjectReference[] + ) => Promise; + }; __LEGACY: ISearchStartLegacy & SearchAggsStartLegacy; } diff --git a/src/plugins/data/public/services.ts b/src/plugins/data/public/services.ts index 199ba17b3b81b..ba0b2de393bde 100644 --- a/src/plugins/data/public/services.ts +++ b/src/plugins/data/public/services.ts @@ -17,7 +17,7 @@ * under the License. */ -import { NotificationsStart, CoreSetup, CoreStart } from 'src/core/public'; +import { NotificationsStart, CoreStart } from 'src/core/public'; import { FieldFormatsStart } from './field_formats'; import { createGetterSetter } from '../../kibana_utils/public'; import { IndexPatternsContract } from './index_patterns'; @@ -48,7 +48,7 @@ export const [getQueryService, setQueryService] = createGetterSetter< >('Query'); export const [getInjectedMetadata, setInjectedMetadata] = createGetterSetter< - CoreSetup['injectedMetadata'] + CoreStart['injectedMetadata'] >('InjectedMetadata'); export const [getSearchService, setSearchService] = createGetterSetter< diff --git a/src/plugins/data/public/types.ts b/src/plugins/data/public/types.ts index 5414de16be310..aaef403979de6 100644 --- a/src/plugins/data/public/types.ts +++ b/src/plugins/data/public/types.ts @@ -75,8 +75,11 @@ export interface IDataPluginServices extends Partial { /** @internal **/ export interface InternalStartServices { - fieldFormats: FieldFormatsStart; - notifications: CoreStart['notifications']; + readonly fieldFormats: FieldFormatsStart; + readonly notifications: CoreStart['notifications']; + readonly uiSettings: CoreStart['uiSettings']; + readonly searchService: DataPublicPluginStart['search']; + readonly injectedMetadata: CoreStart['injectedMetadata']; } /** @internal **/ diff --git a/src/plugins/saved_objects/public/saved_object/helpers/apply_es_resp.ts b/src/plugins/saved_objects/public/saved_object/helpers/apply_es_resp.ts index 9776887b6d741..df687a051fc7d 100644 --- a/src/plugins/saved_objects/public/saved_object/helpers/apply_es_resp.ts +++ b/src/plugins/saved_objects/public/saved_object/helpers/apply_es_resp.ts @@ -17,9 +17,9 @@ * under the License. */ import _ from 'lodash'; -import { EsResponse, SavedObject, SavedObjectConfig } from '../../types'; +import { EsResponse, SavedObject, SavedObjectConfig, SavedObjectKibanaServices } from '../../types'; import { expandShorthand, SavedObjectNotFound } from '../../../../kibana_utils/public'; -import { DataPublicPluginStart, IndexPattern } from '../../../../data/public'; +import { IndexPattern } from '../../../../data/public'; /** * A given response of and ElasticSearch containing a plain saved object is applied to the given @@ -29,7 +29,7 @@ export async function applyESResp( resp: EsResponse, savedObject: SavedObject, config: SavedObjectConfig, - createSearchSource: DataPublicPluginStart['search']['createSearchSource'] + dependencies: SavedObjectKibanaServices ) { const mapping = expandShorthand(config.mapping); const esType = config.type || ''; @@ -65,7 +65,10 @@ export async function applyESResp( if (config.searchSource) { try { - savedObject.searchSource = await createSearchSource(meta.searchSourceJSON, resp.references); + savedObject.searchSource = await dependencies.search.searchSource.fromJSON( + meta.searchSourceJSON, + resp.references + ); } catch (error) { if ( error.constructor.name === 'SavedObjectNotFound' && diff --git a/src/plugins/saved_objects/public/saved_object/helpers/build_saved_object.ts b/src/plugins/saved_objects/public/saved_object/helpers/build_saved_object.ts index e8faef4e9e040..e5b0e18e7b433 100644 --- a/src/plugins/saved_objects/public/saved_object/helpers/build_saved_object.ts +++ b/src/plugins/saved_objects/public/saved_object/helpers/build_saved_object.ts @@ -16,8 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import _ from 'lodash'; -import { SearchSource } from '../../../../data/public'; +import { once } from 'lodash'; import { hydrateIndexPattern } from './hydrate_index_pattern'; import { intializeSavedObject } from './initialize_saved_object'; import { serializeSavedObject } from './serialize_saved_object'; @@ -55,7 +54,9 @@ export function buildSavedObject( savedObject.isSaving = false; savedObject.defaults = config.defaults || {}; // optional search source which this object configures - savedObject.searchSource = config.searchSource ? new SearchSource() : undefined; + savedObject.searchSource = config.searchSource + ? services.search.searchSource.create() + : undefined; // the id of the document savedObject.id = config.id || void 0; // the migration version of the document, should only be set on imports @@ -79,10 +80,9 @@ export function buildSavedObject( * @return {Promise} * @resolved {SavedObject} */ - savedObject.init = _.once(() => intializeSavedObject(savedObject, savedObjectsClient, config)); + savedObject.init = once(() => intializeSavedObject(savedObject, savedObjectsClient, config)); - savedObject.applyESResp = (resp: EsResponse) => - applyESResp(resp, savedObject, config, services.search.createSearchSource); + savedObject.applyESResp = (resp: EsResponse) => applyESResp(resp, savedObject, config, services); /** * Serialize this object diff --git a/src/plugins/saved_objects/public/saved_object/saved_object.test.ts b/src/plugins/saved_objects/public/saved_object/saved_object.test.ts index 60c66f84080b2..f7e67dbe3ee1d 100644 --- a/src/plugins/saved_objects/public/saved_object/saved_object.test.ts +++ b/src/plugins/saved_objects/public/saved_object/saved_object.test.ts @@ -28,9 +28,8 @@ import { // @ts-ignore import StubIndexPattern from 'test_utils/stub_index_pattern'; -import { InvalidJSONProperty } from '../../../kibana_utils/public'; import { coreMock } from '../../../../core/public/mocks'; -import { dataPluginMock } from '../../../../plugins/data/public/mocks'; +import { dataPluginMock, createSearchSourceMock } from '../../../../plugins/data/public/mocks'; import { SavedObjectAttributes, SimpleSavedObject } from 'kibana/public'; import { IIndexPattern } from '../../../data/common/index_patterns'; @@ -40,9 +39,9 @@ describe('Saved Object', () => { const startMock = coreMock.createStart(); const dataStartMock = dataPluginMock.createStartContract(); const saveOptionsMock = {} as SavedObjectSaveOpts; + const savedObjectsClientStub = startMock.savedObjects.client; let SavedObjectClass: new (config: SavedObjectConfig) => SavedObject; - const savedObjectsClientStub = startMock.savedObjects.client; /** * Returns a fake doc response with the given index and id, of type dashboard @@ -99,16 +98,22 @@ describe('Saved Object', () => { function createInitializedSavedObject(config: SavedObjectConfig = {}) { const savedObject = new SavedObjectClass(config); savedObject.title = 'my saved object'; + return savedObject.init!(); } beforeEach(() => { - (dataStartMock.search.createSearchSource as jest.Mock).mockReset(); - SavedObjectClass = createSavedObjectClass({ + SavedObjectClass = createSavedObjectClass(({ savedObjectsClient: savedObjectsClientStub, indexPatterns: dataStartMock.indexPatterns, - search: dataStartMock.search, - } as SavedObjectKibanaServices); + search: { + ...dataStartMock.search, + searchSource: { + ...dataStartMock.search.searchSource, + create: createSearchSourceMock, + }, + }, + } as unknown) as SavedObjectKibanaServices); }); describe('save', () => { @@ -411,27 +416,6 @@ describe('Saved Object', () => { }); }); - it('forwards thrown exceptions from createSearchSource', async () => { - (dataStartMock.search.createSearchSource as jest.Mock).mockImplementation(() => { - throw new InvalidJSONProperty(''); - }); - const savedObject = await createInitializedSavedObject({ - type: 'dashboard', - searchSource: true, - }); - const response = { - found: true, - _source: {}, - }; - - try { - await savedObject.applyESResp(response); - throw new Error('applyESResp should have failed, but did not.'); - } catch (err) { - expect(err instanceof InvalidJSONProperty).toBe(true); - } - }); - it('preserves original defaults if not overridden', () => { const id = 'anid'; const preserveMeValue = 'here to stay!'; @@ -589,42 +573,45 @@ describe('Saved Object', () => { it('passes references to search source parsing function', async () => { const savedObject = new SavedObjectClass({ type: 'dashboard', searchSource: true }); - return savedObject.init!().then(() => { - const searchSourceJSON = JSON.stringify({ - indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.index', - filter: [ - { - meta: { - indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index', - }, - }, - ], - }); - const response = { - found: true, - _source: { - kibanaSavedObjectMeta: { - searchSourceJSON, + await savedObject.init!(); + + const searchSourceJSON = JSON.stringify({ + indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.index', + filter: [ + { + meta: { + indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index', }, }, - references: [ - { - name: 'kibanaSavedObjectMeta.searchSourceJSON.index', - type: 'index-pattern', - id: 'my-index-1', - }, - { - name: 'kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index', - type: 'index-pattern', - id: 'my-index-2', - }, - ], - }; - savedObject.applyESResp(response); - expect(dataStartMock.search.createSearchSource).toBeCalledWith( - searchSourceJSON, - response.references - ); + ], + }); + const response = { + found: true, + _source: { + kibanaSavedObjectMeta: { + searchSourceJSON, + }, + }, + references: [ + { + name: 'kibanaSavedObjectMeta.searchSourceJSON.index', + type: 'index-pattern', + id: 'my-index-1', + }, + { + name: 'kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index', + type: 'index-pattern', + id: 'my-index-2', + }, + ], + }; + const result = await savedObject.applyESResp(response); + + expect(result._source).toEqual({ + kibanaSavedObjectMeta: { + searchSourceJSON: + '{"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index","filter":[{"meta":{"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index"}}]}', + }, }); }); }); diff --git a/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.test.ts b/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.test.ts index 23c2b75169555..f98b1762511f4 100644 --- a/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.test.ts +++ b/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.test.ts @@ -25,6 +25,7 @@ import { } from './resolve_saved_objects'; import { SavedObject, SavedObjectLoader } from '../../../saved_objects/public'; import { IndexPatternsContract } from '../../../data/public'; +import { dataPluginMock } from '../../../data/public/mocks'; class SavedObjectNotFound extends Error { constructor(options: Record) { @@ -233,6 +234,19 @@ describe('resolveSavedObjects', () => { }); describe('resolveIndexPatternConflicts', () => { + let dependencies: Parameters[3]; + + beforeEach(() => { + const search = dataPluginMock.createStartContract().search; + + dependencies = { + indexPatterns: ({ + get: (id: string) => Promise.resolve({ id }), + } as unknown) as IndexPatternsContract, + search, + }; + }); + it('should resave resolutions', async () => { const save = jest.fn(); @@ -284,11 +298,13 @@ describe('resolveSavedObjects', () => { const overwriteAll = false; - await resolveIndexPatternConflicts(resolutions, conflictedIndexPatterns, overwriteAll, ({ - get: (id: string) => Promise.resolve({ id }), - } as unknown) as IndexPatternsContract); - expect(conflictedIndexPatterns[0].obj.searchSource!.getField('index')!.id).toEqual('2'); - expect(conflictedIndexPatterns[1].obj.searchSource!.getField('index')!.id).toEqual('4'); + await resolveIndexPatternConflicts( + resolutions, + conflictedIndexPatterns, + overwriteAll, + dependencies + ); + expect(save.mock.calls.length).toBe(2); expect(save).toHaveBeenCalledWith({ confirmOverwrite: !overwriteAll }); }); @@ -345,13 +361,13 @@ describe('resolveSavedObjects', () => { const overwriteAll = false; - await resolveIndexPatternConflicts(resolutions, conflictedIndexPatterns, overwriteAll, ({ - get: (id: string) => Promise.resolve({ id }), - } as unknown) as IndexPatternsContract); + await resolveIndexPatternConflicts( + resolutions, + conflictedIndexPatterns, + overwriteAll, + dependencies + ); - expect(conflictedIndexPatterns[0].obj.searchSource!.getField('filter')).toEqual([ - { meta: { index: 'newFilterIndex' } }, - ]); expect(save.mock.calls.length).toBe(2); }); }); diff --git a/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.ts b/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.ts index 15e03ed39d88c..d4764b8949a60 100644 --- a/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.ts +++ b/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.ts @@ -21,7 +21,7 @@ import { i18n } from '@kbn/i18n'; import { cloneDeep } from 'lodash'; import { OverlayStart, SavedObjectReference } from 'src/core/public'; import { SavedObject, SavedObjectLoader } from '../../../saved_objects/public'; -import { IndexPatternsContract, IIndexPattern, createSearchSource } from '../../../data/public'; +import { IndexPatternsContract, IIndexPattern, DataPublicPluginStart } from '../../../data/public'; type SavedObjectsRawDoc = Record; @@ -162,7 +162,10 @@ export async function resolveIndexPatternConflicts( resolutions: Array<{ oldId: string; newId: string }>, conflictedIndexPatterns: any[], overwriteAll: boolean, - indexPatterns: IndexPatternsContract + dependencies: { + indexPatterns: IndexPatternsContract; + search: DataPublicPluginStart['search']; + } ) { let importCount = 0; @@ -208,7 +211,7 @@ export async function resolveIndexPatternConflicts( // The user decided to skip this conflict so do nothing return; } - obj.searchSource = await createSearchSource(indexPatterns)( + obj.searchSource = await dependencies.search.searchSource.fromJSON( JSON.stringify(serializedSearchSource), replacedReferences ); diff --git a/src/plugins/saved_objects_management/public/management_section/mount_section.tsx b/src/plugins/saved_objects_management/public/management_section/mount_section.tsx index 6f03f97079bb6..fe3150fc0bb07 100644 --- a/src/plugins/saved_objects_management/public/management_section/mount_section.tsx +++ b/src/plugins/saved_objects_management/public/management_section/mount_section.tsx @@ -187,6 +187,7 @@ const SavedObjectsTablePage = ({ actionRegistry={actionRegistry} savedObjectsClient={coreStart.savedObjects.client} indexPatterns={dataStart.indexPatterns} + search={dataStart.search} http={coreStart.http} overlays={coreStart.overlays} notifications={coreStart.notifications} diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap index fe64df6ff51d1..563ce87b82ae5 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap @@ -256,6 +256,41 @@ exports[`SavedObjectsTable import should show the flyout 1`] = ` "openModal": [MockFunction], } } + search={ + Object { + "__LEGACY": Object { + "AggConfig": [MockFunction], + "AggType": [MockFunction], + "FieldParamType": [MockFunction], + "MetricAggType": [MockFunction], + "aggTypeFieldFilters": AggTypeFieldFilters { + "filters": Set {}, + }, + "esClient": Object { + "msearch": [MockFunction], + "search": [MockFunction], + }, + "parentPipelineAggHelper": [MockFunction], + "siblingPipelineAggHelper": [MockFunction], + }, + "aggs": Object { + "calculateAutoTimeExpression": [Function], + "createAggConfigs": [MockFunction], + "types": Object { + "get": [Function], + "getAll": [Function], + "getBuckets": [Function], + "getMetrics": [Function], + }, + }, + "search": [MockFunction], + "searchSource": Object { + "create": [MockFunction], + "fromJSON": [MockFunction], + }, + "setInterceptor": [MockFunction], + } + } serviceRegistry={ Object { "all": [MockFunction], diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx index 5d713ff044f24..c915a8a2be8f8 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx @@ -33,6 +33,7 @@ import { coreMock } from '../../../../../../core/public/mocks'; import { serviceRegistryMock } from '../../../services/service_registry.mock'; import { Flyout, FlyoutProps, FlyoutState } from './flyout'; import { ShallowWrapper } from 'enzyme'; +import { dataPluginMock } from '../../../../../data/public/mocks'; const mockFile = ({ name: 'foo.ndjson', @@ -56,6 +57,7 @@ describe('Flyout', () => { beforeEach(() => { const { http, overlays } = coreMock.createStart(); + const search = dataPluginMock.createStartContract().search; defaultProps = { close: jest.fn(), @@ -68,6 +70,7 @@ describe('Flyout', () => { http, allowedTypes: ['search', 'index-pattern', 'visualization'], serviceRegistry: serviceRegistryMock.create(), + search, }; }); @@ -499,7 +502,10 @@ describe('Flyout', () => { component.instance().resolutions, mockConflictedIndexPatterns, true, - defaultProps.indexPatterns + { + search: defaultProps.search, + indexPatterns: defaultProps.indexPatterns, + } ); expect(saveObjectsMock).toHaveBeenCalledWith( mockConflictedSavedObjectsLinkedToSavedSearches, diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx index 45788dcb601ae..fbcfeafe291a3 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx @@ -48,7 +48,11 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { OverlayStart, HttpStart } from 'src/core/public'; -import { IndexPatternsContract, IIndexPattern } from '../../../../../data/public'; +import { + IndexPatternsContract, + IIndexPattern, + DataPublicPluginStart, +} from '../../../../../data/public'; import { importFile, importLegacyFile, @@ -75,6 +79,7 @@ export interface FlyoutProps { indexPatterns: IndexPatternsContract; overlays: OverlayStart; http: HttpStart; + search: DataPublicPluginStart['search']; } export interface FlyoutState { @@ -362,7 +367,7 @@ export class Flyout extends Component { failedImports, } = this.state; - const { serviceRegistry, indexPatterns } = this.props; + const { serviceRegistry, indexPatterns, search } = this.props; this.setState({ error: undefined, @@ -388,7 +393,10 @@ export class Flyout extends Component { resolutions, conflictedIndexPatterns!, isOverwriteAllChecked, - indexPatterns + { + indexPatterns, + search, + } ); } this.setState({ diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx index 342fdc4784b09..6b975a9cd1709 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx @@ -88,6 +88,7 @@ describe('SavedObjectsTable', () => { let overlays: ReturnType; let notifications: ReturnType; let savedObjects: ReturnType; + let search: ReturnType['search']; const shallowRender = (overrides: Partial = {}) => { return (shallowWithI18nProvider( @@ -106,6 +107,7 @@ describe('SavedObjectsTable', () => { overlays = overlayServiceMock.createStartContract(); notifications = notificationServiceMock.createStartContract(); savedObjects = savedObjectsServiceMock.createStartContract(); + search = dataPluginMock.createStartContract().search; const applications = applicationServiceMock.createStartContract(); applications.capabilities = { @@ -141,6 +143,7 @@ describe('SavedObjectsTable', () => { perPageConfig: 15, goInspectObject: () => {}, canGoInApp: () => true, + search, }; findObjectsMock.mockImplementation(() => ({ diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx index c76fea5a0fb29..b9ebaf2b236f4 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx @@ -73,6 +73,7 @@ import { SavedObjectsManagementActionServiceStart, } from '../../services'; import { Header, Table, Flyout, Relationships } from './components'; +import { DataPublicPluginStart } from '../../../../../plugins/data/public'; interface ExportAllOption { id: string; @@ -86,6 +87,7 @@ export interface SavedObjectsTableProps { savedObjectsClient: SavedObjectsClientContract; indexPatterns: IndexPatternsContract; http: HttpStart; + search: DataPublicPluginStart['search']; overlays: OverlayStart; notifications: NotificationsStart; applications: ApplicationStart; @@ -467,6 +469,7 @@ export class SavedObjectsTable extends Component ); } diff --git a/src/plugins/saved_objects_management/public/plugin.ts b/src/plugins/saved_objects_management/public/plugin.ts index c8dede3da9263..28eac96dcbf46 100644 --- a/src/plugins/saved_objects_management/public/plugin.ts +++ b/src/plugins/saved_objects_management/public/plugin.ts @@ -116,8 +116,9 @@ export class SavedObjectsManagementPlugin }; } - public start(core: CoreStart) { + public start(core: CoreStart, { data }: StartDependencies) { const actionStart = this.actionService.start(); + return { actions: actionStart, }; diff --git a/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts b/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts index c99c7a4c2caa1..8bc98ca4b4784 100644 --- a/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts +++ b/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts @@ -32,7 +32,7 @@ import { // @ts-ignore import { updateOldState } from '../legacy/vis_update_state'; import { extractReferences, injectReferences } from './saved_visualization_references'; -import { IIndexPattern, ISearchSource, SearchSource } from '../../../../plugins/data/public'; +import { IIndexPattern, ISearchSource } from '../../../../plugins/data/public'; import { ISavedVis, SerializedVis } from '../types'; import { createSavedSearchesLoader } from '../../../../plugins/discover/public'; import { getChrome, getOverlays, getIndexPatterns, getSavedObjects, getSearch } from '../services'; @@ -80,20 +80,24 @@ export const convertFromSerializedVis = (vis: SerializedVis): ISavedVis => { }; const getSearchSource = async (inputSearchSource: ISearchSource, savedSearchId?: string) => { + const search = getSearch(); + const searchSource = inputSearchSource.createCopy ? inputSearchSource.createCopy() - : new SearchSource({ ...(inputSearchSource as any).fields }); + : search.searchSource.create({ ...(inputSearchSource as any).fields }); + if (savedSearchId) { const savedSearch = await createSavedSearchesLoader({ + search, savedObjectsClient: getSavedObjects().client, indexPatterns: getIndexPatterns(), - search: getSearch(), chrome: getChrome(), overlays: getOverlays(), }).get(savedSearchId); searchSource.setParent(savedSearch.searchSource); } + searchSource!.setField('size', 0); return searchSource; }; diff --git a/x-pack/plugins/maps/public/kibana_services.d.ts b/x-pack/plugins/maps/public/kibana_services.d.ts index 867557c296292..3d346fe1acdd5 100644 --- a/x-pack/plugins/maps/public/kibana_services.d.ts +++ b/x-pack/plugins/maps/public/kibana_services.d.ts @@ -3,8 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - -import { IIndexPattern } from 'src/plugins/data/public'; +import { IIndexPattern, DataPublicPluginStart } from 'src/plugins/data/public'; export function getLicenseId(): any; export function getInspector(): any; @@ -30,6 +29,7 @@ export function getUiActions(): any; export function getCore(): any; export function getNavigation(): any; export function getCoreI18n(): any; +export function getSearchService(): DataPublicPluginStart['search']; export function setLicenseId(args: unknown): void; export function setInspector(args: unknown): void; @@ -53,3 +53,4 @@ export function setUiActions(args: unknown): void; export function setCore(args: unknown): void; export function setNavigation(args: unknown): void; export function setCoreI18n(args: unknown): void; +export function setSearchService(args: DataPublicPluginStart['search']): void; diff --git a/x-pack/plugins/maps/public/kibana_services.js b/x-pack/plugins/maps/public/kibana_services.js index dcbd54a09381f..431d7a3b339b7 100644 --- a/x-pack/plugins/maps/public/kibana_services.js +++ b/x-pack/plugins/maps/public/kibana_services.js @@ -5,8 +5,6 @@ */ import { esFilters, search } from '../../../../src/plugins/data/public'; -export { SearchSource } from '../../../../src/plugins/data/public'; - export const SPATIAL_FILTER_TYPE = esFilters.FILTERS.SPATIAL_FILTER; const { getRequestInspectorStats, getResponseInspectorStats } = search; @@ -137,3 +135,7 @@ export const getNavigation = () => navigation; let coreI18n; export const setCoreI18n = kibanaCoreI18n => (coreI18n = kibanaCoreI18n); export const getCoreI18n = () => coreI18n; + +let dataSearchService; +export const setSearchService = searchService => (dataSearchService = searchService); +export const getSearchService = () => dataSearchService; diff --git a/x-pack/plugins/maps/public/layers/sources/es_search_source/es_search_source.js b/x-pack/plugins/maps/public/layers/sources/es_search_source/es_search_source.js index aaa56b30c735a..7d3aa86a0fc56 100644 --- a/x-pack/plugins/maps/public/layers/sources/es_search_source/es_search_source.js +++ b/x-pack/plugins/maps/public/layers/sources/es_search_source/es_search_source.js @@ -10,7 +10,7 @@ import uuid from 'uuid/v4'; import { VECTOR_SHAPE_TYPES } from '../vector_feature_types'; import { AbstractESSource } from '../es_source'; -import { SearchSource } from '../../../kibana_services'; +import { getSearchService } from '../../../kibana_services'; import { VectorStyle } from '../../styles/vector/vector_style'; import { VectorLayer } from '../../vector_layer'; import { hitsToGeoJson } from '../../../elasticsearch_geo_utils'; @@ -427,13 +427,17 @@ export class ESSearchSource extends AbstractESSource { return {}; } - const searchSource = new SearchSource(); + const searchService = getSearchService(); + const searchSource = searchService.searchSource.create(); + searchSource.setField('index', indexPattern); searchSource.setField('size', 1); + const query = { language: 'kuery', query: `_id:"${docId}" and _index:"${index}"`, }; + searchSource.setField('query', query); searchSource.setField('fields', this._getTooltipPropertyNames()); diff --git a/x-pack/plugins/maps/public/layers/sources/es_source/es_source.d.ts b/x-pack/plugins/maps/public/layers/sources/es_source/es_source.d.ts index 3b41ae6bfd86b..d95ec5a64e6c3 100644 --- a/x-pack/plugins/maps/public/layers/sources/es_source/es_source.d.ts +++ b/x-pack/plugins/maps/public/layers/sources/es_source/es_source.d.ts @@ -6,7 +6,7 @@ import { AbstractVectorSource } from '../vector_source'; import { IVectorSource } from '../vector_source'; -import { IndexPattern, SearchSource } from '../../../../../../../src/plugins/data/public'; +import { IndexPattern, ISearchSource } from '../../../../../../../src/plugins/data/public'; import { VectorSourceRequestMeta } from '../../../../common/descriptor_types'; import { VectorStyle } from '../../styles/vector/vector_style'; import { IDynamicStyleProperty } from '../../styles/vector/properties/dynamic_style_property'; @@ -21,7 +21,7 @@ export interface IESSource extends IVectorSource { searchFilters: VectorSourceRequestMeta, limit: number, initialSearchContext?: object - ): Promise; + ): Promise; loadStylePropsMeta( layerName: string, style: VectorStyle, @@ -41,7 +41,7 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource searchFilters: VectorSourceRequestMeta, limit: number, initialSearchContext?: object - ): Promise; + ): Promise; loadStylePropsMeta( layerName: string, style: VectorStyle, diff --git a/x-pack/plugins/maps/public/layers/sources/es_source/es_source.js b/x-pack/plugins/maps/public/layers/sources/es_source/es_source.js index 9ab87577b7780..ccd8bc4a859db 100644 --- a/x-pack/plugins/maps/public/layers/sources/es_source/es_source.js +++ b/x-pack/plugins/maps/public/layers/sources/es_source/es_source.js @@ -9,8 +9,8 @@ import { getAutocompleteService, fetchSearchSourceAndRecordWithInspector, getIndexPatternService, - SearchSource, getTimeFilter, + getSearchService, } from '../../../kibana_services'; import { createExtentFilter } from '../../../elasticsearch_geo_utils'; import _ from 'lodash'; @@ -125,8 +125,9 @@ export class AbstractESSource extends AbstractVectorSource { if (isTimeAware) { allFilters.push(getTimeFilter().createFilter(indexPattern, searchFilters.timeFilters)); } + const searchService = getSearchService(); + const searchSource = searchService.searchSource.create(initialSearchContext); - const searchSource = new SearchSource(initialSearchContext); searchSource.setField('index', indexPattern); searchSource.setField('size', limit); searchSource.setField('filter', allFilters); @@ -135,7 +136,8 @@ export class AbstractESSource extends AbstractVectorSource { } if (searchFilters.sourceQuery) { - const layerSearchSource = new SearchSource(); + const layerSearchSource = searchService.searchSource.create(); + layerSearchSource.setField('index', indexPattern); layerSearchSource.setField('query', searchFilters.sourceQuery); searchSource.setParent(layerSearchSource); @@ -294,7 +296,9 @@ export class AbstractESSource extends AbstractVectorSource { }, {}); const indexPattern = await this.getIndexPattern(); - const searchSource = new SearchSource(); + const searchService = getSearchService(); + const searchSource = searchService.searchSource.create(); + searchSource.setField('index', indexPattern); searchSource.setField('size', 0); searchSource.setField('aggs', aggs); diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index 0b076621326ce..74bd305bff963 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -31,7 +31,7 @@ import { setUiActions, setUiSettings, setVisualizations, - // @ts-ignore + setSearchService, } from './kibana_services'; import { featureCatalogueEntry } from './feature_catalogue_entry'; // @ts-ignore @@ -68,6 +68,7 @@ export const bindStartCoreAndPlugins = (core: CoreStart, plugins: any) => { setFileUpload(fileUpload); setIndexPatternSelect(data.ui.IndexPatternSelect); setTimeFilter(data.query.timefilter.timefilter); + setSearchService(data.search); setIndexPatternService(data.indexPatterns); setAutocompleteService(data.autocomplete); setCore(core); From 126d187429cebac1855f11476b06953d6911f251 Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Wed, 22 Apr 2020 11:01:49 -0600 Subject: [PATCH 02/39] Upgrade to EUI v22.3.0 (#62963) * snapshot updates * bump to eui 22.3.0 * snapshots * Fix css selector in test to match EUI internal DOM change * Updated snapshots * Insert react-beautiful-dnd@10 into top-level kibana to force siem's node_modules to hold its correct version * More snapshot updates, resolved a typescript issue * Move react-beautiful-dnd dependency around to make things happy again * Use SearchFilterConfig from the top-level eui export * Swallow 'ResizeObserver error does not have an err object' error in canvas * Swallow ResizeObserver error cross-browser --- package.json | 2 +- packages/kbn-ui-shared-deps/package.json | 2 +- .../__snapshots__/list_control.test.tsx.snap | 1 + .../dashboard_empty_screen.test.tsx.snap | 8 +- .../index_pattern_select.tsx | 1 + .../__snapshots__/field_name.test.tsx.snap | 12 +- .../components/relationships.tsx | 4 +- .../public/components/agg_group.tsx | 2 +- .../__snapshots__/icon_select.test.js.snap | 1 + .../splits/__snapshots__/terms.test.js.snap | 1 + .../__snapshots__/new_vis_modal.test.tsx.snap | 24 +-- .../wizard/type_selection/type_selection.tsx | 6 +- .../plugins/kbn_tp_run_pipeline/package.json | 2 +- .../kbn_sample_panel_action/package.json | 2 +- .../kbn_tp_custom_visualizations/package.json | 2 +- .../kbn_tp_embeddable_explorer/package.json | 2 +- .../__test__/__snapshots__/List.test.tsx.snap | 10 ++ .../app/ServiceMap/cytoscapeOptions.ts | 2 +- .../Waterfall/SpanFlyout/index.tsx | 2 +- .../Waterfall/SyncBadge.tsx | 2 +- .../__snapshots__/Stackframe.test.tsx.snap | 19 +-- .../Summary/ErrorCountSummaryItemBadge.tsx | 4 +- .../Summary/HttpInfoSummaryItem/index.tsx | 4 +- .../public/components/tag/tag_badge.tsx | 2 +- .../__snapshots__/asset.examples.storyshot | 8 +- .../canvas/public/lib/window_error_handler.js | 13 +- .../collection_enabled.test.js.snap | 2 + .../collection_interval.test.js.snap | 4 + x-pack/legacy/plugins/siem/package.json | 4 +- .../public/components/and_or_badge/index.tsx | 2 +- .../public/components/draggables/index.tsx | 2 +- .../map_tool_tip/line_tool_tip_content.tsx | 2 +- .../siem/public/components/flyout/index.tsx | 2 +- .../public/components/header_page/index.tsx | 2 +- .../public/components/header_page/title.tsx | 2 +- .../note_card_body.test.tsx.snap | 92 ++++++++--- .../siem/public/components/page/index.tsx | 4 +- .../__snapshots__/index.test.tsx.snap | 92 ++++++++--- .../super_date_picker/index.test.tsx | 8 +- .../renderers/suricata/suricata_signature.tsx | 2 +- .../body/renderers/zeek/zeek_signature.tsx | 2 +- .../timeline/data_providers/empty.tsx | 2 +- .../data_providers/provider_badge.tsx | 2 +- .../provider_item_and_drag_drop.tsx | 2 +- .../insert_timeline_popover/index.test.tsx | 10 +- .../timeline/properties/helpers.tsx | 2 +- .../components/description_step/helpers.tsx | 2 +- x-pack/package.json | 5 +- .../action_wizard/action_wizard.tsx | 6 +- .../components/field_manager/field_editor.tsx | 2 +- .../field_manager/field_manager.test.tsx | 6 +- .../editor_frame/chart_switch.test.tsx | 4 +- .../editor_frame/chart_switch.tsx | 6 +- .../__snapshots__/overrides.test.js.snap | 3 + .../__snapshots__/editor.test.tsx.snap | 4 + .../report_listing.test.tsx.snap | 104 ++++++++----- .../cluster_privileges.test.tsx.snap | 1 + .../elasticsearch_privileges.test.tsx.snap | 1 + .../index_privilege_form.test.tsx.snap | 2 + .../common/expression_items/of.test.tsx | 2 + .../functional/page_objects/security_page.js | 2 +- yarn.lock | 143 ++++-------------- 62 files changed, 387 insertions(+), 284 deletions(-) diff --git a/package.json b/package.json index 21e9f67e6206a..a7729b6dab7a1 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,7 @@ "@elastic/charts": "18.3.0", "@elastic/datemath": "5.0.3", "@elastic/ems-client": "7.8.0", - "@elastic/eui": "21.0.1", + "@elastic/eui": "22.3.0", "@elastic/filesaver": "1.1.2", "@elastic/good": "8.1.1-kibana2", "@elastic/numeral": "2.4.0", diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json index c8614b1df9d5d..3aa01179c00f3 100644 --- a/packages/kbn-ui-shared-deps/package.json +++ b/packages/kbn-ui-shared-deps/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@elastic/charts": "18.3.0", - "@elastic/eui": "21.0.1", + "@elastic/eui": "22.3.0", "@kbn/i18n": "1.0.0", "abortcontroller-polyfill": "^1.4.0", "angular": "^1.7.9", diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/list_control.test.tsx.snap b/src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/list_control.test.tsx.snap index 59ae99260cecd..43e2af6d099e8 100644 --- a/src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/list_control.test.tsx.snap +++ b/src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/list_control.test.tsx.snap @@ -46,6 +46,7 @@ exports[`renders ListControl 1`] = ` placeholder="Select..." selectedOptions={Array []} singleSelection={false} + sortMatchesBy="none" /> `; diff --git a/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap b/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap index 7210879c5eacc..1bc85fa110ca0 100644 --- a/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap +++ b/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap @@ -304,13 +304,13 @@ exports[`DashboardEmptyScreen renders correctly with readonly mode 1`] = ` url="/plugins/kibana/home/assets/welcome_graphic_light_2x.png" >
@@ -998,13 +998,13 @@ exports[`DashboardEmptyScreen renders correctly without visualize paragraph 1`] url="/plugins/kibana/home/assets/welcome_graphic_light_2x.png" >
diff --git a/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx b/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx index c56060bb9c288..6eb4f82a940b1 100644 --- a/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx +++ b/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx @@ -27,6 +27,7 @@ import { SavedObjectsClientContract, SimpleSavedObject } from '../../../../../co import { getTitle } from '../../index_patterns/lib'; export type IndexPatternSelectProps = Required< + // Omit, 'isLoading' | 'onSearchChange' | 'options' | 'selectedOptions' | 'append' | 'prepend' | 'sortMatchesBy'>, Omit, 'isLoading' | 'onSearchChange' | 'options' | 'selectedOptions'>, 'onChange' | 'placeholder' > & { diff --git a/src/plugins/discover/public/components/field_name/__snapshots__/field_name.test.tsx.snap b/src/plugins/discover/public/components/field_name/__snapshots__/field_name.test.tsx.snap index 9f48e6e57e0ff..afb541253d994 100644 --- a/src/plugins/discover/public/components/field_name/__snapshots__/field_name.test.tsx.snap +++ b/src/plugins/discover/public/components/field_name/__snapshots__/field_name.test.tsx.snap @@ -8,12 +8,12 @@ exports[`FieldName renders a geo field, useShortDots is set to true 1`] = ` class="euiFlexItem euiFlexItem--flexGrowZero" >
@@ -39,12 +39,12 @@ exports[`FieldName renders a number field by providing a field record, useShortD class="euiFlexItem euiFlexItem--flexGrowZero" >
@@ -70,12 +70,12 @@ exports[`FieldName renders a string field by providing fieldType and fieldName 1 class="euiFlexItem euiFlexItem--flexGrowZero" >
diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.tsx index ddb262138d565..d9e39f31d181a 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.tsx @@ -32,7 +32,7 @@ import { EuiText, EuiSpacer, } from '@elastic/eui'; -import { FilterConfig } from '@elastic/eui/src/components/search_bar/filters/filters'; +import { SearchFilterConfig } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { IBasePath } from 'src/core/public'; @@ -284,7 +284,7 @@ export class Relationships extends Component 1} diff --git a/src/plugins/vis_type_timeseries/public/application/components/icon_select/__snapshots__/icon_select.test.js.snap b/src/plugins/vis_type_timeseries/public/application/components/icon_select/__snapshots__/icon_select.test.js.snap index d269f61beefab..dd454231a9ac2 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/icon_select/__snapshots__/icon_select.test.js.snap +++ b/src/plugins/vis_type_timeseries/public/application/components/icon_select/__snapshots__/icon_select.test.js.snap @@ -85,6 +85,7 @@ exports[`src/legacy/core_plugins/metrics/public/components/icon_select/icon_sele "asPlainText": true, } } + sortMatchesBy="none" /> `; diff --git a/src/plugins/vis_type_timeseries/public/application/components/splits/__snapshots__/terms.test.js.snap b/src/plugins/vis_type_timeseries/public/application/components/splits/__snapshots__/terms.test.js.snap index 78436720dd66c..3bf8b77621cf5 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/splits/__snapshots__/terms.test.js.snap +++ b/src/plugins/vis_type_timeseries/public/application/components/splits/__snapshots__/terms.test.js.snap @@ -223,6 +223,7 @@ exports[`src/legacy/core_plugins/metrics/public/components/splits/terms.test.js "asPlainText": true, } } + sortMatchesBy="none" /> diff --git a/src/plugins/visualizations/public/wizard/__snapshots__/new_vis_modal.test.tsx.snap b/src/plugins/visualizations/public/wizard/__snapshots__/new_vis_modal.test.tsx.snap index 860150d688711..6aef2c2b2ceb7 100644 --- a/src/plugins/visualizations/public/wizard/__snapshots__/new_vis_modal.test.tsx.snap +++ b/src/plugins/visualizations/public/wizard/__snapshots__/new_vis_modal.test.tsx.snap @@ -1214,7 +1214,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1` data-test-subj="visNewDialogTypes" role="menu" > -
- - + - - + - + @@ -2669,7 +2669,7 @@ exports[`NewVisModal should render as expected 1`] = ` data-test-subj="visNewDialogTypes" role="menu" > - - - + - - + - + diff --git a/src/plugins/visualizations/public/wizard/type_selection/type_selection.tsx b/src/plugins/visualizations/public/wizard/type_selection/type_selection.tsx index bb5037545cc82..acf562d5bf363 100644 --- a/src/plugins/visualizations/public/wizard/type_selection/type_selection.tsx +++ b/src/plugins/visualizations/public/wizard/type_selection/type_selection.tsx @@ -27,7 +27,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiKeyPadMenu, - EuiKeyPadMenuItemButton, + EuiKeyPadMenuItem, EuiModalHeader, EuiModalHeaderTitle, EuiScreenReaderOnly, @@ -262,7 +262,7 @@ class TypeSelection extends React.Component{visType.title}} onClick={onClick} @@ -282,7 +282,7 @@ class TypeSelection extends React.Component - + ); }; diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json index 611e16e5a942d..001293be44829 100644 --- a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json +++ b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json @@ -7,7 +7,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "21.0.1", + "@elastic/eui": "22.3.0", "react": "^16.12.0", "react-dom": "^16.12.0" } diff --git a/test/plugin_functional/plugins/kbn_sample_panel_action/package.json b/test/plugin_functional/plugins/kbn_sample_panel_action/package.json index 914ff39884fa3..4ca705137a12c 100644 --- a/test/plugin_functional/plugins/kbn_sample_panel_action/package.json +++ b/test/plugin_functional/plugins/kbn_sample_panel_action/package.json @@ -8,7 +8,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "21.0.1", + "@elastic/eui": "22.3.0", "react": "^16.12.0" }, "scripts": { diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json index 9ee7845816faa..818621ffe5ae5 100644 --- a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json @@ -8,7 +8,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "21.0.1", + "@elastic/eui": "22.3.0", "react": "^16.12.0" }, "scripts": { diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json index 3b3d69c06ff3e..59b7337f5ff07 100644 --- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json +++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json @@ -8,7 +8,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "21.0.1", + "@elastic/eui": "22.3.0", "react": "^16.12.0" }, "scripts": { diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap index afa0cb51cd108..0fbf0a5c7a27d 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap +++ b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap @@ -141,6 +141,7 @@ exports[`ErrorGroupOverview -> List should render empty state 1`] = ` > Group ID @@ -162,6 +163,7 @@ exports[`ErrorGroupOverview -> List should render empty state 1`] = ` > Type @@ -183,6 +185,7 @@ exports[`ErrorGroupOverview -> List should render empty state 1`] = ` > Error message and culprit @@ -231,6 +234,7 @@ exports[`ErrorGroupOverview -> List should render empty state 1`] = ` > Occurrences @@ -272,6 +276,7 @@ exports[`ErrorGroupOverview -> List should render empty state 1`] = ` > Latest occurrence @@ -519,6 +524,7 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = ` > Group ID @@ -540,6 +546,7 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = ` > Type @@ -561,6 +568,7 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = ` > Error message and culprit @@ -609,6 +617,7 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = ` > Occurrences @@ -650,6 +659,7 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = ` > Latest occurrence diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts index bf0e052b951ae..eb71994bd2e47 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts @@ -60,7 +60,7 @@ const style: cytoscape.Stylesheet[] = [ ? theme.euiColorPrimary : theme.euiColorMediumShade, 'border-width': 2, - color: theme.textColors.default, + color: theme.textColors.text, // theme.euiFontFamily doesn't work here for some reason, so we're just // specifying a subset of the fonts for the label text. 'font-family': 'Inter UI, Segoe UI, Helvetica, Arial, sans-serif', diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx index 87102a486ab5f..27095264461ee 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx @@ -74,7 +74,7 @@ function getSpanTypes(span: Span) { const SpanBadge = styled(EuiBadge)` display: inline-block; margin-right: ${px(units.quarter)}; -`; +` as any; const HttpInfoContainer = styled('div')` margin-right: ${px(units.quarter)}; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SyncBadge.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SyncBadge.tsx index 764f15f943ad2..a5d8902ff1626 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SyncBadge.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SyncBadge.tsx @@ -13,7 +13,7 @@ import { px, units } from '../../../../../../style/variables'; const SpanBadge = styled(EuiBadge)` display: inline-block; margin-right: ${px(units.quarter)}; -`; +` as any; interface SyncBadgeProps { /** diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/Stackframe.test.tsx.snap b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/Stackframe.test.tsx.snap index 0aeb2443679fa..4177b54f20385 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/Stackframe.test.tsx.snap +++ b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/Stackframe.test.tsx.snap @@ -192,7 +192,9 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] /> - + -
-
+ diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItemBadge.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItemBadge.tsx index 7558f002c0afc..858e6d29bfa5e 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItemBadge.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItemBadge.tsx @@ -15,9 +15,9 @@ interface Props { count: number; } -const Badge = styled(EuiBadge)` +const Badge = (styled(EuiBadge)` margin-top: ${px(units.eighth)}; -`; +` as any) as any; export const ErrorCountSummaryItemBadge = ({ count }: Props) => ( diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/index.tsx index 94ae9e63644b9..1e1d49b2cf417 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/index.tsx @@ -11,9 +11,9 @@ import styled from 'styled-components'; import { units, px, truncate, unit } from '../../../../style/variables'; import { HttpStatusBadge } from '../HttpStatusBadge'; -const HttpInfoBadge = styled(EuiBadge)` +const HttpInfoBadge = (styled(EuiBadge)` margin-right: ${px(units.quarter)}; -`; +` as any) as any; const Url = styled('span')` display: inline-block; diff --git a/x-pack/legacy/plugins/beats_management/public/components/tag/tag_badge.tsx b/x-pack/legacy/plugins/beats_management/public/components/tag/tag_badge.tsx index be8a56486577e..7fa0231cf3409 100644 --- a/x-pack/legacy/plugins/beats_management/public/components/tag/tag_badge.tsx +++ b/x-pack/legacy/plugins/beats_management/public/components/tag/tag_badge.tsx @@ -31,7 +31,7 @@ export const TagBadge = (props: TagBadgeProps) => { } onClickAriaLabel={onClickAriaLabel} > {idToRender} diff --git a/x-pack/legacy/plugins/canvas/public/components/asset_manager/__examples__/__snapshots__/asset.examples.storyshot b/x-pack/legacy/plugins/canvas/public/components/asset_manager/__examples__/__snapshots__/asset.examples.storyshot index 5cb64964ee38f..6601f570209e9 100644 --- a/x-pack/legacy/plugins/canvas/public/components/asset_manager/__examples__/__snapshots__/asset.examples.storyshot +++ b/x-pack/legacy/plugins/canvas/public/components/asset_manager/__examples__/__snapshots__/asset.examples.storyshot @@ -18,13 +18,13 @@ exports[`Storyshots components/Assets/Asset airplane 1`] = ` className="canvasAsset__thumb canvasCheckered" >
Asset thumbnail
@@ -192,13 +192,13 @@ exports[`Storyshots components/Assets/Asset marker 1`] = ` className="canvasAsset__thumb canvasCheckered" >
Asset thumbnail
diff --git a/x-pack/legacy/plugins/canvas/public/lib/window_error_handler.js b/x-pack/legacy/plugins/canvas/public/lib/window_error_handler.js index bf97928452c71..75307816b4371 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/window_error_handler.js +++ b/x-pack/legacy/plugins/canvas/public/lib/window_error_handler.js @@ -47,9 +47,16 @@ window.canvasInitErrorHandler = () => { window.onerror = (...args) => { const [message, , , , err] = args; - const isKnownError = Object.keys(knownErrors).find(errorName => { - return err.constructor.name === errorName || message.indexOf(errorName) >= 0; - }); + // ResizeObserver error does not have an `err` object + // It is thrown during workpad loading due to layout thrashing + // https://stackoverflow.com/questions/49384120/resizeobserver-loop-limit-exceeded + // https://github.com/elastic/eui/issues/3346 + console.log(message); + const isKnownError = + message.includes('ResizeObserver loop') || + Object.keys(knownErrors).find(errorName => { + return err.constructor.name === errorName || message.indexOf(errorName) >= 0; + }); if (isKnownError) { return; } diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/collection_enabled/__tests__/__snapshots__/collection_enabled.test.js.snap b/x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/collection_enabled/__tests__/__snapshots__/collection_enabled.test.js.snap index 43ff42b6a80e8..fa266b30fcb94 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/collection_enabled/__tests__/__snapshots__/collection_enabled.test.js.snap +++ b/x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/collection_enabled/__tests__/__snapshots__/collection_enabled.test.js.snap @@ -192,6 +192,7 @@ exports[`ExplainCollectionEnabled should explain about xpack.monitoring.collecti isCopyable={false} paddingSize="l" transparentBackground={false} + whiteSpace="pre-wrap" > { expect(store.getState().inputs.global.timerange.kind).toBe('relative'); }); - test('Make Sure it is last 15 minutes date', () => { - expect(store.getState().inputs.global.timerange.fromStr).toBe('now-15m'); + test('Make Sure it is last 24 hours date', () => { + expect(store.getState().inputs.global.timerange.fromStr).toBe('now-24h'); expect(store.getState().inputs.global.timerange.toStr).toBe('now'); }); @@ -180,7 +180,7 @@ describe('SIEM Super Date Picker', () => { ).toBe('Today'); }); - test('Today and Last 15 minutes are in Recently used date ranges', () => { + test('Today and Last 24 hours are in Recently used date ranges', () => { wrapper .find('[data-test-subj="superDatePickerToggleQuickMenuButton"]') .first() @@ -198,7 +198,7 @@ describe('SIEM Super Date Picker', () => { .find('div.euiQuickSelectPopover__section') .at(1) .text() - ).toBe('Last 15 minutesToday'); + ).toBe('Last 24 hoursToday'); }); test('Make sure that it does not add any duplicate if you click again on today', () => { diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/suricata/suricata_signature.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/suricata/suricata_signature.tsx index 24c52f3372d62..d05ab6bcc378f 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/suricata/suricata_signature.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/suricata/suricata_signature.tsx @@ -30,7 +30,7 @@ SignatureFlexItem.displayName = 'SignatureFlexItem'; const Badge = styled(EuiBadge)` vertical-align: top; -`; +` as any; // eslint-disable-line @typescript-eslint/no-explicit-any Badge.displayName = 'Badge'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/zeek/zeek_signature.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/zeek/zeek_signature.tsx index 39c21c4ffa33b..31525a4904bc2 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/zeek/zeek_signature.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/zeek/zeek_signature.tsx @@ -21,7 +21,7 @@ import * as i18n from './translations'; const Badge = styled(EuiBadge)` vertical-align: top; -`; +` as any; // eslint-disable-line @typescript-eslint/no-explicit-any Badge.displayName = 'Badge'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/empty.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/empty.tsx index 56639f90c1464..ddb07b4636b88 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/empty.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/empty.tsx @@ -26,7 +26,7 @@ const BadgeHighlighted = styled(EuiBadge)` margin: 0 5px 0 5px; maxwidth: 85px; minwidth: 85px; -`; +` as any; // eslint-disable-line @typescript-eslint/no-explicit-any BadgeHighlighted.displayName = 'BadgeHighlighted'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/provider_badge.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/provider_badge.tsx index 8e4665acc2c26..a5525df5ef3c3 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/provider_badge.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/provider_badge.tsx @@ -44,7 +44,7 @@ const ProviderBadgeStyled = styled(EuiBadge)` margin-right: 0; margin-left: 4px; } -`; +` as any; // eslint-disable-line @typescript-eslint/no-explicit-any ProviderBadgeStyled.displayName = 'ProviderBadgeStyled'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/provider_item_and_drag_drop.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/provider_item_and_drag_drop.tsx index 663b3dd501341..0ae952412a973 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/provider_item_and_drag_drop.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/provider_item_and_drag_drop.tsx @@ -56,7 +56,7 @@ DropAndTargetDataProviders.displayName = 'DropAndTargetDataProviders'; const NumberProviderAndBadge = styled(EuiBadge)` margin: 0px 5px; -`; +` as any; // eslint-disable-line @typescript-eslint/no-explicit-any NumberProviderAndBadge.displayName = 'NumberProviderAndBadge'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/insert_timeline_popover/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/insert_timeline_popover/index.test.tsx index e63bce388ae80..c5aea833a4b2f 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/insert_timeline_popover/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/insert_timeline_popover/index.test.tsx @@ -12,9 +12,13 @@ import routeData from 'react-router'; import { InsertTimelinePopoverComponent } from './'; const mockDispatch = jest.fn(); -jest.mock('react-redux', () => ({ - useDispatch: () => mockDispatch, -})); +jest.mock('react-redux', () => { + const reactRedux = jest.requireActual('react-redux'); + return { + ...reactRedux, + useDispatch: () => mockDispatch, + }; +}); const mockLocation = { pathname: '/apath', hash: '', diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/properties/helpers.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/properties/helpers.tsx index 6f7e1f782d3f6..bf953cfd006aa 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/properties/helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/properties/helpers.tsx @@ -39,7 +39,7 @@ export const newTimelineToolTip = 'Create a new timeline'; const NotesCountBadge = styled(EuiBadge)` margin-left: 5px; -`; +` as any; // eslint-disable-line @typescript-eslint/no-explicit-any NotesCountBadge.displayName = 'NotesCountBadge'; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx index 5b7a85e23834d..d3014354ab7b3 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx @@ -41,7 +41,7 @@ const EuiBadgeWrap = styled(EuiBadge)` .euiBadge__text { white-space: pre-wrap !important; } -`; +` as any; // eslint-disable-line @typescript-eslint/no-explicit-any export const buildQueryBarDescription = ({ field, diff --git a/x-pack/package.json b/x-pack/package.json index 3c6146b491f60..7d2dcb789c2ad 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -91,6 +91,7 @@ "@types/proper-lockfile": "^3.0.1", "@types/puppeteer": "^1.20.1", "@types/react": "^16.9.19", + "@types/react-beautiful-dnd": "^12.1.1", "@types/react-dom": "^16.9.5", "@types/react-redux": "^7.1.7", "@types/react-router-dom": "^5.1.3", @@ -182,7 +183,7 @@ "@elastic/apm-rum-react": "^1.1.1", "@elastic/datemath": "5.0.3", "@elastic/ems-client": "7.8.0", - "@elastic/eui": "21.0.1", + "@elastic/eui": "22.3.0", "@elastic/filesaver": "1.1.2", "@elastic/maki": "6.2.0", "@elastic/node-crypto": "1.1.1", @@ -305,7 +306,7 @@ "re-resizable": "^6.1.1", "react": "^16.12.0", "react-apollo": "^2.1.4", - "react-beautiful-dnd": "^8.0.7", + "react-beautiful-dnd": "^12.2.0", "react-datetime": "^2.14.0", "react-dom": "^16.12.0", "react-dropzone": "^4.2.9", diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx index 41ef863c00e44..ef4a0f76de9ed 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx @@ -12,7 +12,7 @@ import { EuiIcon, EuiSpacer, EuiText, - EuiKeyPadMenuItemButton, + EuiKeyPadMenuItem, } from '@elastic/eui'; import { txtChangeButton } from './i18n'; import './action_wizard.scss'; @@ -180,7 +180,7 @@ const ActionFactorySelector: React.FC = ({ return ( {actionFactories.map(actionFactory => ( - = ({ onClick={() => onActionFactorySelected(actionFactory)} > {actionFactory.iconType && } - + ))} ); diff --git a/x-pack/plugins/graph/public/components/field_manager/field_editor.tsx b/x-pack/plugins/graph/public/components/field_manager/field_editor.tsx index 78e4180aa2b2a..211458e67d05b 100644 --- a/x-pack/plugins/graph/public/components/field_manager/field_editor.tsx +++ b/x-pack/plugins/graph/public/components/field_manager/field_editor.tsx @@ -129,7 +129,7 @@ export function FieldEditor({ })} onClickAriaLabel={badgeDescription} title="" - onClick={e => { + onClick={(e: React.MouseEvent) => { if (e.shiftKey) { toggleDisabledState(); } else { diff --git a/x-pack/plugins/graph/public/components/field_manager/field_manager.test.tsx b/x-pack/plugins/graph/public/components/field_manager/field_manager.test.tsx index 948f89705a5d5..ac656ebdd9512 100644 --- a/x-pack/plugins/graph/public/components/field_manager/field_manager.test.tsx +++ b/x-pack/plugins/graph/public/components/field_manager/field_manager.test.tsx @@ -252,7 +252,11 @@ describe('field_manager', () => { act(() => { getDisplayForm() .find(EuiColorPicker) - .prop('onChange')!('#aaa'); + .prop('onChange')!('#aaa', { + rgba: [170, 170, 170, 1], + hex: '#aaa', + isValid: true, + }); }); fieldEditor.update(); getDisplayForm() diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/chart_switch.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/chart_switch.test.tsx index 6698c9e68b98c..87c359eb1345b 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/chart_switch.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/chart_switch.test.tsx @@ -10,7 +10,7 @@ import { mountWithIntl as mount } from 'test_utils/enzyme_helpers'; import { ReactWrapper } from 'enzyme'; import { ChartSwitch } from './chart_switch'; import { Visualization, FramePublicAPI, DatasourcePublicAPI } from '../../types'; -import { EuiKeyPadMenuItemButton } from '@elastic/eui'; +import { EuiKeyPadMenuItem } from '@elastic/eui'; import { Action } from './state_management'; describe('chart_switch', () => { @@ -129,7 +129,7 @@ describe('chart_switch', () => { function getMenuItem(subType: string, component: ReactWrapper) { showFlyout(component); return component - .find(EuiKeyPadMenuItemButton) + .find(EuiKeyPadMenuItem) .find(`[data-test-subj="lnsChartSwitchPopover_${subType}"]`) .first(); } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/chart_switch.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/chart_switch.tsx index 5e2fced577724..6bf7983d32023 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/chart_switch.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/chart_switch.tsx @@ -10,7 +10,7 @@ import { EuiPopover, EuiPopoverTitle, EuiKeyPadMenu, - EuiKeyPadMenuItemButton, + EuiKeyPadMenuItem, EuiButtonEmpty, } from '@elastic/eui'; import { flatten } from 'lodash'; @@ -215,7 +215,7 @@ export function ChartSwitch(props: Props) { {(visualizationTypes || []).map(v => ( - {v.label}
} role="menuitem" @@ -238,7 +238,7 @@ export function ChartSwitch(props: Props) { betaBadgeIconType={v.selection.dataLoss !== 'nothing' ? 'alert' : undefined} > - + ))} diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/edit_flyout/__snapshots__/overrides.test.js.snap b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/edit_flyout/__snapshots__/overrides.test.js.snap index 46428ff9c351a..59481cb086566 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/edit_flyout/__snapshots__/overrides.test.js.snap +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/edit_flyout/__snapshots__/overrides.test.js.snap @@ -70,6 +70,7 @@ exports[`Overrides render overrides 1`] = ` "asPlainText": true, } } + sortMatchesBy="none" /> diff --git a/x-pack/plugins/ml/public/application/jobs/components/custom_url_editor/__snapshots__/editor.test.tsx.snap b/x-pack/plugins/ml/public/application/jobs/components/custom_url_editor/__snapshots__/editor.test.tsx.snap index 1b27d09965c4e..06ee16f264756 100644 --- a/x-pack/plugins/ml/public/application/jobs/components/custom_url_editor/__snapshots__/editor.test.tsx.snap +++ b/x-pack/plugins/ml/public/application/jobs/components/custom_url_editor/__snapshots__/editor.test.tsx.snap @@ -150,6 +150,7 @@ exports[`CustomUrlEditor renders the editor for a dashboard type URL with a labe placeholder="Select entities" selectedOptions={Array []} singleSelection={false} + sortMatchesBy="none" /> - - Report - + + + Report + + @@ -246,11 +249,14 @@ Array [
- - Created at - + + + Created at + +
@@ -273,11 +279,14 @@ Array [
- - Status - + + + Status + +
@@ -298,11 +307,14 @@ Array [
- - Actions - + + + Actions + +
@@ -514,11 +526,14 @@ Array [
- - Report - + + + Report + +
@@ -541,11 +556,14 @@ Array [
- - Created at - + + + Created at + +
@@ -568,11 +586,14 @@ Array [
- - Status - + + + Status + +
@@ -593,11 +614,14 @@ Array [
- - Actions - + + + Actions + +
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/cluster_privileges.test.tsx.snap b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/cluster_privileges.test.tsx.snap index a52438ca93638..dd27fe13e84a3 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/cluster_privileges.test.tsx.snap +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/cluster_privileges.test.tsx.snap @@ -29,6 +29,7 @@ exports[`it renders without crashing 1`] = ` } selectedOptions={Array []} singleSelection={false} + sortMatchesBy="none" /> diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap index b306de5b84093..46fb9b8572679 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap @@ -119,6 +119,7 @@ exports[`it renders without crashing 1`] = ` placeholder="Add a user…" selectedOptions={Array []} singleSelection={false} + sortMatchesBy="none" /> diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/index_privilege_form.test.tsx.snap b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/index_privilege_form.test.tsx.snap index bbf90d0f64bd2..d23a6da13a3bb 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/index_privilege_form.test.tsx.snap +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/index_privilege_form.test.tsx.snap @@ -37,6 +37,7 @@ exports[`it renders without crashing 1`] = ` options={Array []} selectedOptions={Array []} singleSelection={false} + sortMatchesBy="none" /> @@ -82,6 +83,7 @@ exports[`it renders without crashing 1`] = ` } selectedOptions={Array []} singleSelection={false} + sortMatchesBy="none" /> diff --git a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/of.test.tsx b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/of.test.tsx index 2f3c0000ef96b..b01104a8d5cf7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/of.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/of.test.tsx @@ -45,6 +45,7 @@ describe('of expression', () => { "asPlainText": true, } } + sortMatchesBy="none" /> `); }); @@ -105,6 +106,7 @@ describe('of expression', () => { "asPlainText": true, } } + sortMatchesBy="none" /> `); }); diff --git a/x-pack/test/functional/page_objects/security_page.js b/x-pack/test/functional/page_objects/security_page.js index b399327012a77..84eb0cc378771 100644 --- a/x-pack/test/functional/page_objects/security_page.js +++ b/x-pack/test/functional/page_objects/security_page.js @@ -385,7 +385,7 @@ export function SecurityPageProvider({ getService, getPageObjects }) { // have to remove the '*' return find .clickByCssSelector( - 'div[data-test-subj="fieldInput0"] .euiBadge[title="*"] svg.euiIcon' + 'div[data-test-subj="fieldInput0"] [title="Remove * from selection in this group"] svg.euiIcon' ) .then(function() { return addGrantedField(userObj.elasticsearch.indices[0].field_security.grant); diff --git a/yarn.lock b/yarn.lock index b47befbf9057b..30747ee555fe2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1088,7 +1088,7 @@ pirates "^4.0.0" source-map-support "^0.5.16" -"@babel/runtime-corejs2@^7.2.0", "@babel/runtime-corejs2@^7.4.2", "@babel/runtime-corejs2@^7.6.3": +"@babel/runtime-corejs2@^7.2.0", "@babel/runtime-corejs2@^7.6.3": version "7.9.2" resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.9.2.tgz#f11d074ff99b9b4319b5ecf0501f12202bf2bf4d" integrity sha512-ayjSOxuK2GaSDJFCtLgHnYjuMyIpViNujWrZo8GUpN60/n7juzJKK5yOo6RFVb0zdU9ACJFK+MsZrUnj3OmXMw== @@ -1096,14 +1096,6 @@ core-js "^2.6.5" regenerator-runtime "^0.13.4" -"@babel/runtime@7.0.0-beta.54": - version "7.0.0-beta.54" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0-beta.54.tgz#39ebb42723fe7ca4b3e1b00e967e80138d47cadf" - integrity sha1-Oeu0JyP+fKSz4bAOln6AE41Hyt8= - dependencies: - core-js "^2.5.7" - regenerator-runtime "^0.12.0" - "@babel/runtime@7.3.4": version "7.3.4" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.4.tgz#73d12ba819e365fcf7fd152aed56d6df97d21c83" @@ -1314,16 +1306,16 @@ tabbable "^1.1.0" uuid "^3.1.0" -"@elastic/eui@21.0.1": - version "21.0.1" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-21.0.1.tgz#7cf6846ed88032aebd72f75255298df2fbe26554" - integrity sha512-Hf8ZGRI265qpOKwnnqhZkaMQvali+Xg6FAaNZSskkpXvdLhwGtUGC4YU7HW2vb7svq6IpNUuz+5XWrMLLzVY9w== +"@elastic/eui@22.3.0": + version "22.3.0" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-22.3.0.tgz#31a7a0aaf69b329acff2791fca677a824b63539d" + integrity sha512-LoQd11RoD6cbDuVQhwTr3lR4Jga8D5cBGaKFPzaS8MoxW5vCu0gcosjI6O5SqmCwifyfU4JP5zeOjy8TawJFxw== dependencies: - "@types/chroma-js" "^1.4.3" + "@types/chroma-js" "^2.0.0" "@types/enzyme" "^3.1.13" "@types/lodash" "^4.14.116" "@types/numeral" "^0.0.25" - "@types/react-beautiful-dnd" "^10.1.0" + "@types/react-beautiful-dnd" "^12.1.2" "@types/react-input-autosize" "^2.0.2" "@types/react-virtualized" "^9.18.7" chroma-js "^2.0.4" @@ -1335,7 +1327,7 @@ numeral "^2.0.6" prop-types "^15.6.0" react-ace "^7.0.5" - react-beautiful-dnd "^10.1.0" + react-beautiful-dnd "^13.0.0" react-focus-lock "^1.17.7" react-input-autosize "^2.2.2" react-is "~16.3.0" @@ -3767,10 +3759,10 @@ resolved "https://registry.yarnpkg.com/@types/chroma-js/-/chroma-js-1.4.2.tgz#3152c8dedfa8621f1ccaaabb40722a8aca808bcf" integrity sha512-Ni8yCN1vF0yfnfKf5bNrBm+92EdZIX2sUk+A4t4QvO1x/9G04rGyC0nik4i5UcNfx8Q7MhX4XUDcy2nrkKQLFg== -"@types/chroma-js@^1.4.3": - version "1.4.3" - resolved "https://registry.yarnpkg.com/@types/chroma-js/-/chroma-js-1.4.3.tgz#4456e5cb46885a4952324e55a4b6d4064904790c" - integrity sha512-m33zg9cRLtuaUSzlbMrr7iLIKNzrD4+M6Unt5+9mCu4BhR5NwnRjVKblINCwzcBXooukIgld8DtEncP8qpvbNg== +"@types/chroma-js@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/chroma-js/-/chroma-js-2.0.0.tgz#b0fc98c8625d963f14e8138e0a7961103303ab22" + integrity sha512-iomunXsXjDxhm2y1OeJt8NwmgC7RyNkPAOddlYVGsbGoX8+1jYt84SG4/tf6RWcwzROLx1kPXPE95by1s+ebIg== "@types/chromedriver@^2.38.0": version "2.38.0" @@ -4546,13 +4538,6 @@ "@types/history" "*" "@types/react" "*" -"@types/react-beautiful-dnd@^10.1.0": - version "10.1.1" - resolved "https://registry.yarnpkg.com/@types/react-beautiful-dnd/-/react-beautiful-dnd-10.1.1.tgz#7afae39a4247f30c13b8bbb726ccd1b8cda9d4a5" - integrity sha512-75XELhEIWKTkyd1GdVZFvS1MtJwDs9tM37BbIat8mevcw+uH5dcJzZiwESHIWAzySHawS48nkKCQk/bEDp13Mw== - dependencies: - "@types/react" "*" - "@types/react-beautiful-dnd@^12.1.1": version "12.1.1" resolved "https://registry.yarnpkg.com/@types/react-beautiful-dnd/-/react-beautiful-dnd-12.1.1.tgz#149e638c0f912eee6b74ea419b26bb43d0b1da60" @@ -4560,6 +4545,13 @@ dependencies: "@types/react" "*" +"@types/react-beautiful-dnd@^12.1.2": + version "12.1.2" + resolved "https://registry.yarnpkg.com/@types/react-beautiful-dnd/-/react-beautiful-dnd-12.1.2.tgz#dfd1bdb072e92c1363e5f7a4c1842eaf95f77b21" + integrity sha512-h+0mA4cHmzL4BhyCniB6ZSSZhfO9LpXXbnhdAfa2k7klS03woiOT+Dh5AchY6eoQXk3vQVtqn40YY3u+MwFs8A== + dependencies: + "@types/react" "*" + "@types/react-color@^3.0.1": version "3.0.1" resolved "https://registry.yarnpkg.com/@types/react-color/-/react-color-3.0.1.tgz#5433e2f503ea0e0831cbc6fd0c20f8157d93add0" @@ -9873,7 +9865,7 @@ core-js@^1.0.0: resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY= -core-js@^2.2.0, core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.1, core-js@^2.5.3, core-js@^2.5.7, core-js@^2.6.5, core-js@^2.6.9: +core-js@^2.2.0, core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.1, core-js@^2.5.3, core-js@^2.6.5, core-js@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2" integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A== @@ -10206,18 +10198,6 @@ cson@5.1.0: requirefresh "^2.1.0" safefs "^4.1.0" -css-box-model@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.0.0.tgz#60142814f2b25be00c4aac65ea1a55a531b18922" - integrity sha512-MGipbCM6/HGmsOwN6Enq1OvNKy8H5Q1XKoyBszxwv2efly7ZVg+HcFILX8O6S0xfj27l1+6P7FyCjcQ90m5HBQ== - -css-box-model@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.1.1.tgz#c9fd8e7a8b1d59d41d6812fd1765433f671b2ee0" - integrity sha512-ZxbuLFeAPEDb0wPbGfT7783Vb00MVAkvOlMKwr0kA2PD5EGxk6P3MAhedvVuyVJCWb54bb+6HQ7pdPYENf8AZw== - dependencies: - tiny-invariant "^1.0.3" - css-box-model@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.2.0.tgz#3a26377b4162b3200d2ede4b064ec5b6a75186d0" @@ -20305,21 +20285,11 @@ mem@^4.0.0: mimic-fn "^1.0.0" p-is-promise "^1.1.0" -memoize-one@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.0.2.tgz#3fb8db695aa14ab9c0f1644e1585a8806adc1aee" - integrity sha512-ucx2DmXTeZTsS4GPPUZCbULAN7kdPT1G+H49Y34JjbQ5ESc6OGhVxKvb1iKhr9v19ZB9OtnHwNnhUnNR/7Wteg== - memoize-one@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.0.1.tgz#35a709ffb6e5f0cb79f9679a96f09ec3a35addfa" integrity sha512-S3plzyksLOSF4pkf1Xlb7mA8ZRKZlgp3ebg7rULbfwPT8Ww7uZz5CbLgRKaR92GeXpsNiFbfCRWf/uOrCYIbRg== -memoize-one@^5.0.1: - version "5.0.2" - resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.0.2.tgz#6aba5276856d72fb44ead3efab86432f94ba203d" - integrity sha512-o7lldN4fs/axqctc03NF+PMhd2veRrWeJ2n2GjEzUPBD4F9rmNg4A+bQCACIzwjHJEXuYv4aFFMaH35KZfHUrw== - memoize-one@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0" @@ -23618,15 +23588,6 @@ prop-types@15.6.0: loose-envify "^1.3.1" object-assign "^4.1.1" -prop-types@15.6.1: - version "15.6.1" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca" - integrity sha512-4ec7bY1Y66LymSUOH/zARVYObB23AT2h8cf6e/O6ZALB/N0sqZFEx7rq6EYPX2MkOdKORuooI/H5k9TlR4q7kQ== - dependencies: - fbjs "^0.8.16" - loose-envify "^1.3.1" - object-assign "^4.1.1" - prop-types@15.7.2, prop-types@15.x, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" @@ -24251,20 +24212,6 @@ react-apollo@^2.1.4: lodash "^4.17.10" prop-types "^15.6.0" -react-beautiful-dnd@^10.1.0: - version "10.1.1" - resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-10.1.1.tgz#d753088d77d7632e77cf8a8935fafcffa38f574b" - integrity sha512-TdE06Shfp56wm28EzjgC56EEMgGI5PDHejJ2bxuAZvZr8CVsbksklsJC06Hxf0MSL7FHbflL/RpkJck9isuxHg== - dependencies: - "@babel/runtime-corejs2" "^7.4.2" - css-box-model "^1.1.1" - memoize-one "^5.0.1" - prop-types "^15.6.1" - raf-schd "^4.0.0" - react-redux "^5.0.7" - redux "^4.0.1" - tiny-invariant "^1.0.4" - react-beautiful-dnd@^12.2.0: version "12.2.0" resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-12.2.0.tgz#e5f6222f9e7934c6ed4ee09024547f9e353ae423" @@ -24278,20 +24225,18 @@ react-beautiful-dnd@^12.2.0: redux "^4.0.4" use-memo-one "^1.1.1" -react-beautiful-dnd@^8.0.7: - version "8.0.7" - resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-8.0.7.tgz#2cc7ba62bffe08d3dad862fd8f48204440901b43" - integrity sha512-j2cClhKuACXp/KcG+YXSrVxZ7AQl13dG9X+ojstR6H2G0yoA+1GZn/O147PWVVScmfk/mSt60GNseH7vjae7vQ== +react-beautiful-dnd@^13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-13.0.0.tgz#f70cc8ff82b84bc718f8af157c9f95757a6c3b40" + integrity sha512-87It8sN0ineoC3nBW0SbQuTFXM6bUqM62uJGY4BtTf0yzPl8/3+bHMWkgIe0Z6m8e+gJgjWxefGRVfpE3VcdEg== dependencies: - "@babel/runtime" "7.0.0-beta.54" - css-box-model "^1.0.0" - memoize-one "^4.0.0" - prop-types "15.6.1" - raf-schd "^4.0.0" - react-motion "^0.5.2" - react-redux "^5.0.7" - redux "^4.0.0" - tiny-invariant "^0.0.3" + "@babel/runtime" "^7.8.4" + css-box-model "^1.2.0" + memoize-one "^5.1.1" + raf-schd "^4.0.2" + react-redux "^7.1.1" + redux "^4.0.4" + use-memo-one "^1.1.1" react-clientside-effect@^1.2.0: version "1.2.0" @@ -24658,15 +24603,6 @@ react-motion@^0.4.8: prop-types "^15.5.8" raf "^3.1.0" -react-motion@^0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/react-motion/-/react-motion-0.5.2.tgz#0dd3a69e411316567927917c6626551ba0607316" - integrity sha512-9q3YAvHoUiWlP3cK0v+w1N5Z23HXMj4IF4YuvjvWegWqNPfLXsOBE/V7UvQGpXxHFKRQQcNcVQE31g9SB/6qgQ== - dependencies: - performance-now "^0.2.0" - prop-types "^15.5.8" - raf "^3.1.0" - react-onclickoutside@^6.5.0: version "6.7.1" resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.7.1.tgz#6a5b5b8b4eae6b776259712c89c8a2b36b17be93" @@ -24733,7 +24669,7 @@ react-portal@^3.2.0: dependencies: prop-types "^15.5.8" -react-redux@^5.0.7, react-redux@^5.1.2: +react-redux@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.1.2.tgz#b19cf9e21d694422727bf798e934a916c4080f57" integrity sha512-Ns1G0XXc8hDyH/OcBHOxNgQx9ayH3SPxBnFCOidGKSle8pKihysQw2rG/PmciUQRoclhVBO8HMhiRmGXnDja9Q== @@ -25456,14 +25392,6 @@ redux@^4.0.0: loose-envify "^1.1.0" symbol-observable "^1.2.0" -redux@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.1.tgz#436cae6cc40fbe4727689d7c8fae44808f1bfef5" - integrity sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg== - dependencies: - loose-envify "^1.4.0" - symbol-observable "^1.2.0" - redux@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.4.tgz#4ee1aeb164b63d6a1bcc57ae4aa0b6e6fa7a3796" @@ -29092,12 +29020,7 @@ tiny-inflate@^1.0.0, tiny-inflate@^1.0.2: resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.2.tgz#93d9decffc8805bd57eae4310f0b745e9b6fb3a7" integrity sha1-k9nez/yIBb1X6uQxDwt0Xptvs6c= -tiny-invariant@^0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-0.0.3.tgz#4c7283c950e290889e9e94f64d3586ec9156cf44" - integrity sha512-SA2YwvDrCITM9fTvHTHRpq9W6L2fBsClbqm3maT5PZux4Z73SPPDYwJMtnoWh6WMgmCkJij/LaOlWiqJqFMK8g== - -tiny-invariant@^1.0.2, tiny-invariant@^1.0.3, tiny-invariant@^1.0.4: +tiny-invariant@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.0.4.tgz#346b5415fd93cb696b0c4e8a96697ff590f92463" integrity sha512-lMhRd/djQJ3MoaHEBrw8e2/uM4rs9YMNk0iOr8rHQ0QdbM7D4l0gFl3szKdeixrlyfm9Zqi4dxHCM2qVG8ND5g== From 8b6f6700b74efbfeb1ff117b324efce739430d91 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Wed, 22 Apr 2020 13:02:49 -0400 Subject: [PATCH 03/39] Ensure time picker is actually disabled (#63709) Co-authored-by: Elastic Machine --- .../plugins/monitoring/public/directives/main/index.html | 3 ++- .../legacy/plugins/monitoring/public/directives/main/index.js | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/monitoring/public/directives/main/index.html b/x-pack/legacy/plugins/monitoring/public/directives/main/index.html index bb34cf6f5bb63..f5c4bf5c757af 100644 --- a/x-pack/legacy/plugins/monitoring/public/directives/main/index.html +++ b/x-pack/legacy/plugins/monitoring/public/directives/main/index.html @@ -4,7 +4,8 @@ name="monitoringMain.navName" app-name="'monitoring'" show-search-bar="true" - show-date-picker="true" + show-auto-refresh-only="!monitoringMain.datePicker.enableTimeFilter" + show-date-picker="monitoringMain.datePicker.enableTimeFilter" date-range-from="monitoringMain.datePicker.timeRange.from" date-range-to="monitoringMain.datePicker.timeRange.to" is-refresh-paused="monitoringMain.datePicker.refreshInterval.pause" diff --git a/x-pack/legacy/plugins/monitoring/public/directives/main/index.js b/x-pack/legacy/plugins/monitoring/public/directives/main/index.js index 2505f651d9803..4e09225cb85e8 100644 --- a/x-pack/legacy/plugins/monitoring/public/directives/main/index.js +++ b/x-pack/legacy/plugins/monitoring/public/directives/main/index.js @@ -138,6 +138,7 @@ export class MonitoringMainController { } this.datePicker = { + enableTimeFilter: timefilter.isTimeRangeSelectorEnabled(), timeRange: timefilter.getTime(), refreshInterval: timefilter.getRefreshInterval(), onRefreshChange: ({ isPaused, refreshInterval }, skipSet = false) => { From e708b415c97541764f24b7dcb94fedcbb3fb73f8 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Wed, 22 Apr 2020 13:03:37 -0400 Subject: [PATCH 04/39] [Monitoring] Use custom route to ensure global state is preserved (#63891) * WIP * PR updates * Fix testing Co-authored-by: Elastic Machine --- .../public/components/apm/instances/instances.js | 6 +++++- .../public/components/beats/listing/listing.js | 8 ++------ .../components/cluster/overview/elasticsearch_panel.js | 3 ++- .../public/components/cluster/overview/license_text.js | 3 ++- .../public/components/elasticsearch/ccr/ccr.js | 6 ++++-- .../public/components/elasticsearch/indices/indices.js | 6 +++++- .../public/components/elasticsearch/nodes/nodes.js | 3 ++- .../elasticsearch/shard_activity/recovery_index.js | 3 ++- .../public/components/kibana/instances/instances.js | 3 ++- .../monitoring/public/lib/get_safe_for_external_link.ts | 9 +++++++++ .../monitoring/public/np_imports/angular/modules.ts | 4 +++- 11 files changed, 38 insertions(+), 16 deletions(-) create mode 100644 x-pack/legacy/plugins/monitoring/public/lib/get_safe_for_external_link.ts diff --git a/x-pack/legacy/plugins/monitoring/public/components/apm/instances/instances.js b/x-pack/legacy/plugins/monitoring/public/components/apm/instances/instances.js index f3a888bf9e905..a48fbc51341f1 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/apm/instances/instances.js +++ b/x-pack/legacy/plugins/monitoring/public/components/apm/instances/instances.js @@ -18,6 +18,7 @@ import { } from '@elastic/eui'; import { Status } from './status'; import { formatMetric } from '../../../lib/format_number'; +import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link'; import { formatTimestampToDuration } from '../../../../common'; import { i18n } from '@kbn/i18n'; import { APM_SYSTEM_ID } from '../../../../common/constants'; @@ -56,7 +57,10 @@ function getColumns(setupMode) { return ( - + {name} {setupModeStatus} diff --git a/x-pack/legacy/plugins/monitoring/public/components/beats/listing/listing.js b/x-pack/legacy/plugins/monitoring/public/components/beats/listing/listing.js index dfc9117ef48bc..a20728eb9a58f 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/beats/listing/listing.js +++ b/x-pack/legacy/plugins/monitoring/public/components/beats/listing/listing.js @@ -19,13 +19,13 @@ import { formatMetric } from 'plugins/monitoring/lib/format_number'; import { EuiMonitoringTable } from 'plugins/monitoring/components/table'; import { i18n } from '@kbn/i18n'; import { BEATS_SYSTEM_ID } from '../../../../common/constants'; +import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link'; import { ListingCallOut } from '../../setup_mode/listing_callout'; import { SetupModeBadge } from '../../setup_mode/badge'; import { FormattedMessage } from '@kbn/i18n/react'; export class Listing extends PureComponent { getColumns() { - const { kbnUrl, scope } = this.props.angular; const setupMode = this.props.setupMode; return [ @@ -59,11 +59,7 @@ export class Listing extends PureComponent { return (
{ - scope.$evalAsync(() => { - kbnUrl.changePath(`/beats/beat/${beat.uuid}`); - }); - }} + href={getSafeForExternalLink(`#/beats/beat/${beat.uuid}`)} data-test-subj={`beatLink-${name}`} > {name} diff --git a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js b/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js index 7b08c89f53881..48ff8edaafe60 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js +++ b/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js @@ -32,6 +32,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { Reason } from '../../logs/reason'; import { SetupModeTooltip } from '../../setup_mode/tooltip'; +import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link'; import { ELASTICSEARCH_SYSTEM_ID } from '../../../../common/constants'; const calculateShards = shards => { @@ -168,7 +169,7 @@ export function ElasticsearchPanel(props) { const showMlJobs = () => { // if license doesn't support ML, then `ml === null` if (props.ml) { - const gotoURL = '#/elasticsearch/ml_jobs'; + const gotoURL = getSafeForExternalLink('#/elasticsearch/ml_jobs'); return ( <> diff --git a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/license_text.js b/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/license_text.js index c6fb386c755f3..012c81e63931e 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/license_text.js +++ b/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/license_text.js @@ -6,6 +6,7 @@ import React from 'react'; import moment from 'moment-timezone'; +import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link'; import { capitalize } from 'lodash'; import { EuiLink } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -18,7 +19,7 @@ export function LicenseText({ license, showLicenseExpiration }) { } return ( - + { return ( - + {shardId} ); diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/indices/indices.js b/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/indices/indices.js index f8dd7b0af7a17..a73dfbf8cd321 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/indices/indices.js +++ b/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/indices/indices.js @@ -8,6 +8,7 @@ import React from 'react'; import { capitalize } from 'lodash'; import { LARGE_FLOAT, LARGE_BYTES, LARGE_ABBREVIATED } from '../../../../common/formatting'; import { formatMetric } from '../../../lib/format_number'; +import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link'; import { ElasticsearchStatusIcon } from '../status_icon'; import { ClusterStatus } from '../cluster_status'; import { EuiMonitoringTable } from '../../table'; @@ -34,7 +35,10 @@ const columns = [ sortable: true, render: value => (
- + {value}
diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js b/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js index d9cf29f73ce0d..9e6252315f429 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js +++ b/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js @@ -7,6 +7,7 @@ import React, { Fragment } from 'react'; import { NodeStatusIcon } from '../node'; import { extractIp } from '../../../lib/extract_ip'; // TODO this is only used for elasticsearch nodes summary / node detail, so it should be moved to components/elasticsearch/nodes/lib +import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link'; import { ClusterStatus } from '../cluster_status'; import { EuiMonitoringSSPTable } from '../../table'; import { MetricCell, OfflineCell } from './cells'; @@ -75,7 +76,7 @@ const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid) => { render: (value, node) => { let nameLink = ( {value} diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/recovery_index.js b/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/recovery_index.js index 7a9d096437d52..27598bee6d841 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/recovery_index.js +++ b/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/recovery_index.js @@ -8,13 +8,14 @@ import React from 'react'; import { EuiLink } from '@elastic/eui'; import { Snapshot } from './snapshot'; import { FormattedMessage } from '@kbn/i18n/react'; +import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link'; export const RecoveryIndex = props => { const { name, shard, relocationType } = props; return (
- {name} + {name}
{ return (
{name} diff --git a/x-pack/legacy/plugins/monitoring/public/lib/get_safe_for_external_link.ts b/x-pack/legacy/plugins/monitoring/public/lib/get_safe_for_external_link.ts new file mode 100644 index 0000000000000..6f3e398c1414f --- /dev/null +++ b/x-pack/legacy/plugins/monitoring/public/lib/get_safe_for_external_link.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. + */ + +export function getSafeForExternalLink(url: string) { + return `${url.split('?')[0]}?${location.hash.split('?')[1]}`; +} diff --git a/x-pack/legacy/plugins/monitoring/public/np_imports/angular/modules.ts b/x-pack/legacy/plugins/monitoring/public/np_imports/angular/modules.ts index b506784bf15ee..a047c25c2b1d7 100644 --- a/x-pack/legacy/plugins/monitoring/public/np_imports/angular/modules.ts +++ b/x-pack/legacy/plugins/monitoring/public/np_imports/angular/modules.ts @@ -26,6 +26,7 @@ import { import { PromiseServiceCreator } from './providers/promises'; // @ts-ignore import { PrivateProvider } from './providers/private'; +import { getSafeForExternalLink } from '../../lib/get_safe_for_external_link'; type IPrivate = (provider: (...injectable: any[]) => T) => T; @@ -134,7 +135,8 @@ function createHrefModule(core: AppMountContext['core']) { pre: (_$scope, _$el, $attr) => { $attr.$observe(name, val => { if (val) { - $attr.$set('href', core.http.basePath.prepend(val as string)); + const url = getSafeForExternalLink(val as string); + $attr.$set('href', core.http.basePath.prepend(url)); } }); }, From fc1f4f2695f780a53fdc282130a4ac9608e34de9 Mon Sep 17 00:00:00 2001 From: Josh Dover Date: Wed, 22 Apr 2020 11:14:21 -0600 Subject: [PATCH 05/39] =?UTF-8?q?Do=20not=20serve=20legacy=20JS=20when=20s?= =?UTF-8?q?erving=20a=20Kibana=20Platform=20applicat=E2=80=A6=20(#61011)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/kbn-optimizer/src/cli.ts | 10 +- packages/kbn-optimizer/src/common/bundle.ts | 2 +- ...les.test.ts => get_plugin_bundles.test.ts} | 6 +- .../{get_bundles.ts => get_plugin_bundles.ts} | 2 +- .../src/optimizer/optimizer_config.test.ts | 28 ++- .../src/optimizer/optimizer_config.ts | 24 ++- .../src/worker/webpack.config.ts | 2 +- src/cli/cluster/run_kbn_optimizer.ts | 1 + src/core/public/{core.css => _core.scss} | 0 src/core/public/application/ui/app_router.tsx | 2 +- src/core/public/core_system.test.ts | 12 +- src/core/public/core_system.ts | 13 +- src/core/public/entry_point.ts | 59 +++++++ src/core/public/index.scss | 16 +- .../__snapshots__/legacy_service.test.ts.snap | 35 ---- src/core/public/legacy/legacy_service.test.ts | 160 +++++++++--------- src/core/public/legacy/legacy_service.ts | 49 +++--- .../public/rendering/_base.scss} | 2 +- src/core/public/rendering/_index.scss | 1 + .../saved_objects/saved_objects_client.ts | 16 +- .../tasks/build_kibana_platform_plugins.js | 1 + .../tests_bundle/tests_entry_template.js | 11 +- src/legacy/ui/public/_index.scss | 1 - src/legacy/ui/public/chrome/_index.scss | 3 - src/legacy/ui/public/chrome/_variables.scss | 4 - .../ui/public/chrome/directives/_index.scss | 1 - .../ui/ui_bundles/app_entry_template.js | 7 +- .../ui/ui_bundles/ui_bundles_controller.js | 7 - .../ui/ui_render/bootstrap/template.js.hbs | 18 +- src/legacy/ui/ui_render/ui_render_mixin.js | 59 +++++-- src/optimize/bundles_route/bundles_route.js | 9 +- .../public/angular/templates/_graph.scss | 2 - x-pack/plugins/graph/public/application.ts | 2 + x-pack/plugins/graph/public/index.scss | 2 + 34 files changed, 335 insertions(+), 232 deletions(-) rename packages/kbn-optimizer/src/optimizer/{get_bundles.test.ts => get_plugin_bundles.test.ts} (93%) rename packages/kbn-optimizer/src/optimizer/{get_bundles.ts => get_plugin_bundles.ts} (93%) rename src/core/public/{core.css => _core.scss} (100%) create mode 100644 src/core/public/entry_point.ts delete mode 100644 src/core/public/legacy/__snapshots__/legacy_service.test.ts.snap rename src/{legacy/ui/public/chrome/directives/_kbn_chrome.scss => core/public/rendering/_base.scss} (96%) create mode 100644 src/core/public/rendering/_index.scss delete mode 100644 src/legacy/ui/public/chrome/_index.scss delete mode 100644 src/legacy/ui/public/chrome/_variables.scss delete mode 100644 src/legacy/ui/public/chrome/directives/_index.scss diff --git a/packages/kbn-optimizer/src/cli.ts b/packages/kbn-optimizer/src/cli.ts index dcb4dcd35698d..c0bb408d60d8f 100644 --- a/packages/kbn-optimizer/src/cli.ts +++ b/packages/kbn-optimizer/src/cli.ts @@ -44,6 +44,11 @@ run( throw createFlagError('expected --cache to have no value'); } + const includeCoreBundle = flags.core ?? true; + if (typeof includeCoreBundle !== 'boolean') { + throw createFlagError('expected --core to have no value'); + } + const dist = flags.dist ?? false; if (typeof dist !== 'boolean') { throw createFlagError('expected --dist to have no value'); @@ -87,6 +92,7 @@ run( profileWebpack, extraPluginScanDirs, inspectWorkers, + includeCoreBundle, }); await runOptimizer(config) @@ -95,9 +101,10 @@ run( }, { flags: { - boolean: ['watch', 'oss', 'examples', 'dist', 'cache', 'profile', 'inspect-workers'], + boolean: ['core', 'watch', 'oss', 'examples', 'dist', 'cache', 'profile', 'inspect-workers'], string: ['workers', 'scan-dir'], default: { + core: true, examples: true, cache: true, 'inspect-workers': true, @@ -107,6 +114,7 @@ run( --workers max number of workers to use --oss only build oss plugins --profile profile the webpack builds and write stats.json files to build outputs + --no-core disable generating the core bundle --no-cache disable the cache --no-examples don't build the example plugins --dist create bundles that are suitable for inclusion in the Kibana distributable diff --git a/packages/kbn-optimizer/src/common/bundle.ts b/packages/kbn-optimizer/src/common/bundle.ts index f1bc0965a46cc..7581b90d60af2 100644 --- a/packages/kbn-optimizer/src/common/bundle.ts +++ b/packages/kbn-optimizer/src/common/bundle.ts @@ -23,7 +23,7 @@ import { BundleCache } from './bundle_cache'; import { UnknownVals } from './ts_helpers'; import { includes, ascending, entriesToObject } from './array_helpers'; -const VALID_BUNDLE_TYPES = ['plugin' as const]; +const VALID_BUNDLE_TYPES = ['plugin' as const, 'entry' as const]; export interface BundleSpec { readonly type: typeof VALID_BUNDLE_TYPES[0]; diff --git a/packages/kbn-optimizer/src/optimizer/get_bundles.test.ts b/packages/kbn-optimizer/src/optimizer/get_plugin_bundles.test.ts similarity index 93% rename from packages/kbn-optimizer/src/optimizer/get_bundles.test.ts rename to packages/kbn-optimizer/src/optimizer/get_plugin_bundles.test.ts index 9d95d883d605c..36dc0ca64c6ca 100644 --- a/packages/kbn-optimizer/src/optimizer/get_bundles.test.ts +++ b/packages/kbn-optimizer/src/optimizer/get_plugin_bundles.test.ts @@ -19,13 +19,13 @@ import { createAbsolutePathSerializer } from '@kbn/dev-utils'; -import { getBundles } from './get_bundles'; +import { getPluginBundles } from './get_plugin_bundles'; expect.addSnapshotSerializer(createAbsolutePathSerializer('/repo')); -it('returns a bundle for each plugin', () => { +it('returns a bundle for core and each plugin', () => { expect( - getBundles( + getPluginBundles( [ { directory: '/repo/plugins/foo', diff --git a/packages/kbn-optimizer/src/optimizer/get_bundles.ts b/packages/kbn-optimizer/src/optimizer/get_plugin_bundles.ts similarity index 93% rename from packages/kbn-optimizer/src/optimizer/get_bundles.ts rename to packages/kbn-optimizer/src/optimizer/get_plugin_bundles.ts index 7cd7bf15317e0..4741cc3c30af7 100644 --- a/packages/kbn-optimizer/src/optimizer/get_bundles.ts +++ b/packages/kbn-optimizer/src/optimizer/get_plugin_bundles.ts @@ -23,7 +23,7 @@ import { Bundle } from '../common'; import { KibanaPlatformPlugin } from './kibana_platform_plugins'; -export function getBundles(plugins: KibanaPlatformPlugin[], repoRoot: string) { +export function getPluginBundles(plugins: KibanaPlatformPlugin[], repoRoot: string) { return plugins .filter(p => p.isUiPlugin) .map( diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts b/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts index cc564dd4a8387..d4152133f289d 100644 --- a/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts +++ b/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts @@ -19,7 +19,7 @@ jest.mock('./assign_bundles_to_workers.ts'); jest.mock('./kibana_platform_plugins.ts'); -jest.mock('./get_bundles.ts'); +jest.mock('./get_plugin_bundles.ts'); import Path from 'path'; import Os from 'os'; @@ -90,6 +90,7 @@ describe('OptimizerConfig::parseOptions()', () => { Object { "cache": true, "dist": false, + "includeCoreBundle": false, "inspectWorkers": false, "maxWorkerCount": 2, "pluginPaths": Array [], @@ -114,6 +115,7 @@ describe('OptimizerConfig::parseOptions()', () => { Object { "cache": false, "dist": false, + "includeCoreBundle": false, "inspectWorkers": false, "maxWorkerCount": 2, "pluginPaths": Array [], @@ -138,6 +140,7 @@ describe('OptimizerConfig::parseOptions()', () => { Object { "cache": true, "dist": false, + "includeCoreBundle": false, "inspectWorkers": false, "maxWorkerCount": 2, "pluginPaths": Array [], @@ -164,6 +167,7 @@ describe('OptimizerConfig::parseOptions()', () => { Object { "cache": true, "dist": false, + "includeCoreBundle": false, "inspectWorkers": false, "maxWorkerCount": 2, "pluginPaths": Array [], @@ -187,6 +191,7 @@ describe('OptimizerConfig::parseOptions()', () => { Object { "cache": true, "dist": false, + "includeCoreBundle": false, "inspectWorkers": false, "maxWorkerCount": 2, "pluginPaths": Array [], @@ -210,6 +215,7 @@ describe('OptimizerConfig::parseOptions()', () => { Object { "cache": true, "dist": false, + "includeCoreBundle": false, "inspectWorkers": false, "maxWorkerCount": 100, "pluginPaths": Array [], @@ -230,6 +236,7 @@ describe('OptimizerConfig::parseOptions()', () => { Object { "cache": false, "dist": false, + "includeCoreBundle": false, "inspectWorkers": false, "maxWorkerCount": 100, "pluginPaths": Array [], @@ -250,6 +257,7 @@ describe('OptimizerConfig::parseOptions()', () => { Object { "cache": false, "dist": false, + "includeCoreBundle": false, "inspectWorkers": false, "maxWorkerCount": 100, "pluginPaths": Array [], @@ -271,6 +279,7 @@ describe('OptimizerConfig::parseOptions()', () => { Object { "cache": false, "dist": false, + "includeCoreBundle": false, "inspectWorkers": false, "maxWorkerCount": 100, "pluginPaths": Array [], @@ -292,6 +301,7 @@ describe('OptimizerConfig::parseOptions()', () => { Object { "cache": true, "dist": false, + "includeCoreBundle": false, "inspectWorkers": false, "maxWorkerCount": 100, "pluginPaths": Array [], @@ -314,7 +324,7 @@ describe('OptimizerConfig::create()', () => { .assignBundlesToWorkers; const findKibanaPlatformPlugins: jest.Mock = jest.requireMock('./kibana_platform_plugins.ts') .findKibanaPlatformPlugins; - const getBundles: jest.Mock = jest.requireMock('./get_bundles.ts').getBundles; + const getPluginBundles: jest.Mock = jest.requireMock('./get_plugin_bundles.ts').getPluginBundles; beforeEach(() => { if ('mock' in OptimizerConfig.parseOptions) { @@ -326,7 +336,7 @@ describe('OptimizerConfig::create()', () => { { config: Symbol('worker config 2') }, ]); findKibanaPlatformPlugins.mockReturnValue(Symbol('new platform plugins')); - getBundles.mockReturnValue(Symbol('bundles')); + getPluginBundles.mockReturnValue([Symbol('bundle1'), Symbol('bundle2')]); jest.spyOn(OptimizerConfig, 'parseOptions').mockImplementation((): any => ({ cache: Symbol('parsed cache'), @@ -348,7 +358,10 @@ describe('OptimizerConfig::create()', () => { expect(config).toMatchInlineSnapshot(` OptimizerConfig { - "bundles": Symbol(bundles), + "bundles": Array [ + Symbol(bundle1), + Symbol(bundle2), + ], "cache": Symbol(parsed cache), "dist": Symbol(parsed dist), "inspectWorkers": Symbol(parsed inspect workers), @@ -383,7 +396,7 @@ describe('OptimizerConfig::create()', () => { } `); - expect(getBundles.mock).toMatchInlineSnapshot(` + expect(getPluginBundles.mock).toMatchInlineSnapshot(` Object { "calls": Array [ Array [ @@ -400,7 +413,10 @@ describe('OptimizerConfig::create()', () => { "results": Array [ Object { "type": "return", - "value": Symbol(bundles), + "value": Array [ + Symbol(bundle1), + Symbol(bundle2), + ], }, ], } diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts index 7e1514058446b..d6336cf867470 100644 --- a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts +++ b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts @@ -23,7 +23,7 @@ import Os from 'os'; import { Bundle, WorkerConfig } from '../common'; import { findKibanaPlatformPlugins, KibanaPlatformPlugin } from './kibana_platform_plugins'; -import { getBundles } from './get_bundles'; +import { getPluginBundles } from './get_plugin_bundles'; function pickMaxWorkerCount(dist: boolean) { // don't break if cpus() returns nothing, or an empty array @@ -60,6 +60,9 @@ interface Options { pluginScanDirs?: string[]; /** absolute paths that should be added to the default scan dirs */ extraPluginScanDirs?: string[]; + + /** flag that causes the core bundle to be built along with plugins */ + includeCoreBundle?: boolean; } interface ParsedOptions { @@ -72,6 +75,7 @@ interface ParsedOptions { pluginPaths: string[]; pluginScanDirs: string[]; inspectWorkers: boolean; + includeCoreBundle: boolean; } export class OptimizerConfig { @@ -83,6 +87,7 @@ export class OptimizerConfig { const profileWebpack = !!options.profileWebpack; const inspectWorkers = !!options.inspectWorkers; const cache = options.cache !== false && !process.env.KBN_OPTIMIZER_NO_CACHE; + const includeCoreBundle = !!options.includeCoreBundle; const repoRoot = options.repoRoot; if (!Path.isAbsolute(repoRoot)) { @@ -134,13 +139,28 @@ export class OptimizerConfig { pluginScanDirs, pluginPaths, inspectWorkers, + includeCoreBundle, }; } static create(inputOptions: Options) { const options = OptimizerConfig.parseOptions(inputOptions); const plugins = findKibanaPlatformPlugins(options.pluginScanDirs, options.pluginPaths); - const bundles = getBundles(plugins, options.repoRoot); + const bundles = [ + ...(options.includeCoreBundle + ? [ + new Bundle({ + type: 'entry', + id: 'core', + entry: './public/entry_point', + sourceRoot: options.repoRoot, + contextDir: Path.resolve(options.repoRoot, 'src/core'), + outputDir: Path.resolve(options.repoRoot, 'src/core/target/public'), + }), + ] + : []), + ...getPluginBundles(plugins, options.repoRoot), + ]; return new OptimizerConfig( bundles, diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index 2f81d92ec923c..bb58bd0a3f005 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -104,7 +104,7 @@ export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) { output: { path: bundle.outputDir, - filename: '[name].plugin.js', + filename: `[name].${bundle.type}.js`, publicPath: PUBLIC_PATH_PLACEHOLDER, devtoolModuleFilenameTemplate: info => `/${bundle.type}:${bundle.id}/${Path.relative( diff --git a/src/cli/cluster/run_kbn_optimizer.ts b/src/cli/cluster/run_kbn_optimizer.ts index 7752d4a45ab65..b811fc1f6b294 100644 --- a/src/cli/cluster/run_kbn_optimizer.ts +++ b/src/cli/cluster/run_kbn_optimizer.ts @@ -34,6 +34,7 @@ export function runKbnOptimizer(opts: Record, config: LegacyConfig) const optimizerConfig = OptimizerConfig.create({ repoRoot: REPO_ROOT, watch: true, + includeCoreBundle: true, oss: !!opts.oss, examples: !!opts.runExamples, pluginPaths: config.get('plugins.paths'), diff --git a/src/core/public/core.css b/src/core/public/_core.scss similarity index 100% rename from src/core/public/core.css rename to src/core/public/_core.scss diff --git a/src/core/public/application/ui/app_router.tsx b/src/core/public/application/ui/app_router.tsx index 61c8bc3cadae5..4c135c5769067 100644 --- a/src/core/public/application/ui/app_router.tsx +++ b/src/core/public/application/ui/app_router.tsx @@ -21,7 +21,7 @@ import React, { FunctionComponent, useMemo } from 'react'; import { Route, RouteComponentProps, Router, Switch } from 'react-router-dom'; import { History } from 'history'; import { Observable } from 'rxjs'; -import { useObservable } from 'react-use'; +import useObservable from 'react-use/lib/useObservable'; import { AppLeaveHandler, AppStatus, Mounter } from '../types'; import { AppContainer } from './app_container'; diff --git a/src/core/public/core_system.test.ts b/src/core/public/core_system.test.ts index 94fa74f4bd861..a42719417a2b1 100644 --- a/src/core/public/core_system.test.ts +++ b/src/core/public/core_system.test.ts @@ -59,7 +59,6 @@ const defaultCoreSystemParams = { warnLegacyBrowsers: true, }, } as any, - requireLegacyFiles: jest.fn(), }; beforeEach(() => { @@ -104,19 +103,22 @@ describe('constructor', () => { }); }); - it('passes requireLegacyFiles, useLegacyTestHarness, and a dom element to LegacyPlatformService', () => { + it('passes required params to LegacyPlatformService', () => { const requireLegacyFiles = { requireLegacyFiles: true }; - const useLegacyTestHarness = { useLegacyTestHarness: true }; + const requireLegacyBootstrapModule = { requireLegacyBootstrapModule: true }; + const requireNewPlatformShimModule = { requireNewPlatformShimModule: true }; createCoreSystem({ requireLegacyFiles, - useLegacyTestHarness, + requireLegacyBootstrapModule, + requireNewPlatformShimModule, }); expect(LegacyPlatformServiceConstructor).toHaveBeenCalledTimes(1); expect(LegacyPlatformServiceConstructor).toHaveBeenCalledWith({ requireLegacyFiles, - useLegacyTestHarness, + requireLegacyBootstrapModule, + requireNewPlatformShimModule, }); }); diff --git a/src/core/public/core_system.ts b/src/core/public/core_system.ts index 5c10d89459128..e58114b69dcc1 100644 --- a/src/core/public/core_system.ts +++ b/src/core/public/core_system.ts @@ -17,8 +17,6 @@ * under the License. */ -import './core.css'; - import { CoreId } from '../server'; import { PackageInfo, EnvironmentMode } from '../server/types'; import { CoreSetup, CoreStart } from '.'; @@ -50,8 +48,9 @@ interface Params { rootDomElement: HTMLElement; browserSupportsCsp: boolean; injectedMetadata: InjectedMetadataParams['injectedMetadata']; - requireLegacyFiles: LegacyPlatformParams['requireLegacyFiles']; - useLegacyTestHarness?: LegacyPlatformParams['useLegacyTestHarness']; + requireLegacyFiles?: LegacyPlatformParams['requireLegacyFiles']; + requireLegacyBootstrapModule?: LegacyPlatformParams['requireLegacyBootstrapModule']; + requireNewPlatformShimModule?: LegacyPlatformParams['requireNewPlatformShimModule']; } /** @internal */ @@ -111,7 +110,8 @@ export class CoreSystem { browserSupportsCsp, injectedMetadata, requireLegacyFiles, - useLegacyTestHarness, + requireLegacyBootstrapModule, + requireNewPlatformShimModule, } = params; this.rootDomElement = rootDomElement; @@ -145,7 +145,8 @@ export class CoreSystem { this.legacy = new LegacyPlatformService({ requireLegacyFiles, - useLegacyTestHarness, + requireLegacyBootstrapModule, + requireNewPlatformShimModule, }); } diff --git a/src/core/public/entry_point.ts b/src/core/public/entry_point.ts new file mode 100644 index 0000000000000..9461acccf30b9 --- /dev/null +++ b/src/core/public/entry_point.ts @@ -0,0 +1,59 @@ +/* + * 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. + */ + +/** + * This is the entry point used to boot the frontend when serving a application + * that lives in the Kibana Platform. + * + * Any changes to this file should be kept in sync with + * src/legacy/ui/ui_bundles/app_entry_template.js + */ + +import './index.scss'; +import { i18n } from '@kbn/i18n'; +import { CoreSystem } from './core_system'; + +const injectedMetadata = JSON.parse( + document.querySelector('kbn-injected-metadata')!.getAttribute('data')! +); + +if (process.env.IS_KIBANA_DISTRIBUTABLE !== 'true' && process.env.ELASTIC_APM_ACTIVE === 'true') { + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { init } = require('@elastic/apm-rum'); + init(injectedMetadata.vars.apmConfig); +} + +i18n + .load(injectedMetadata.i18n.translationsUrl) + .catch(e => e) + .then(async i18nError => { + const coreSystem = new CoreSystem({ + injectedMetadata, + rootDomElement: document.body, + browserSupportsCsp: !(window as any).__kbnCspNotEnforced__, + }); + + const setup = await coreSystem.setup(); + if (i18nError && setup) { + setup.fatalErrors.add(i18nError); + } + + await coreSystem.start(); + }); diff --git a/src/core/public/index.scss b/src/core/public/index.scss index 86f2efdff7702..4be46899cff67 100644 --- a/src/core/public/index.scss +++ b/src/core/public/index.scss @@ -1,11 +1,11 @@ -// Functions need to be first, since we use them in our variables and mixin definitions -@import '@elastic/eui/src/global_styling/functions/index'; - -// Variables come next, and are used in some mixins -@import '@elastic/eui/src/global_styling/variables/index'; - -// Mixins provide generic code expansion through helpers -@import '@elastic/eui/src/global_styling/mixins/index'; +// This file is built by both the legacy and KP build systems so we need to +// import this explicitly +@import '../../legacy/ui/public/styles/_styling_constants'; +@import './core'; @import './chrome/index'; @import './overlays/index'; +@import './rendering/index'; + +// Global styles need to be migrated +@import '../../legacy/ui/public/styles/_legacy/_index'; diff --git a/src/core/public/legacy/__snapshots__/legacy_service.test.ts.snap b/src/core/public/legacy/__snapshots__/legacy_service.test.ts.snap deleted file mode 100644 index 97629fdd1add5..0000000000000 --- a/src/core/public/legacy/__snapshots__/legacy_service.test.ts.snap +++ /dev/null @@ -1,35 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`#start() load order useLegacyTestHarness = false loads ui/modules before ui/chrome, and both before legacy files 1`] = ` -Array [ - "ui/new_platform", - "ui/chrome", - "legacy files", -] -`; - -exports[`#start() load order useLegacyTestHarness = true loads ui/modules before ui/test_harness, and both before legacy files 1`] = ` -Array [ - "ui/new_platform", - "ui/test_harness", - "legacy files", -] -`; - -exports[`#stop() destroys the angular scope and empties the targetDomElement if angular is bootstrapped to targetDomElement 1`] = ` -
-`; - -exports[`#stop() does nothing if angular was not bootstrapped to targetDomElement 1`] = ` -
- - -

- this should not be removed -

- - -
-`; diff --git a/src/core/public/legacy/legacy_service.test.ts b/src/core/public/legacy/legacy_service.test.ts index c3de645c6b17e..fa29320aab4e6 100644 --- a/src/core/public/legacy/legacy_service.test.ts +++ b/src/core/public/legacy/legacy_service.test.ts @@ -19,34 +19,6 @@ import angular from 'angular'; -const mockLoadOrder: string[] = []; - -const mockUiNewPlatformSetup = jest.fn(); -const mockUiNewPlatformStart = jest.fn(); -jest.mock('ui/new_platform', () => { - mockLoadOrder.push('ui/new_platform'); - return { - __setup__: mockUiNewPlatformSetup, - __start__: mockUiNewPlatformStart, - }; -}); - -const mockUiChromeBootstrap = jest.fn(); -jest.mock('ui/chrome', () => { - mockLoadOrder.push('ui/chrome'); - return { - bootstrap: mockUiChromeBootstrap, - }; -}); - -const mockUiTestHarnessBootstrap = jest.fn(); -jest.mock('ui/test_harness', () => { - mockLoadOrder.push('ui/test_harness'); - return { - bootstrap: mockUiTestHarnessBootstrap, - }; -}); - import { chromeServiceMock } from '../chrome/chrome_service.mock'; import { fatalErrorsServiceMock } from '../fatal_errors/fatal_errors_service.mock'; import { httpServiceMock } from '../http/http_service.mock'; @@ -69,10 +41,24 @@ const injectedMetadataSetup = injectedMetadataServiceMock.createSetupContract(); const notificationsSetup = notificationServiceMock.createSetupContract(); const uiSettingsSetup = uiSettingsServiceMock.createSetupContract(); +const mockLoadOrder: string[] = []; +const mockUiNewPlatformSetup = jest.fn(); +const mockUiNewPlatformStart = jest.fn(); +const mockUiChromeBootstrap = jest.fn(); const defaultParams = { requireLegacyFiles: jest.fn(() => { mockLoadOrder.push('legacy files'); }), + requireLegacyBootstrapModule: jest.fn(() => { + mockLoadOrder.push('ui/chrome'); + return { + bootstrap: mockUiChromeBootstrap, + }; + }), + requireNewPlatformShimModule: jest.fn(() => ({ + __setup__: mockUiNewPlatformSetup, + __start__: mockUiNewPlatformStart, + })), }; const defaultSetupDeps = { @@ -128,7 +114,7 @@ afterEach(() => { describe('#setup()', () => { describe('default', () => { - it('initializes ui/new_platform with core APIs', () => { + it('initializes new platform shim module with core APIs', () => { const legacyPlatform = new LegacyPlatformService({ ...defaultParams, }); @@ -138,6 +124,21 @@ describe('#setup()', () => { expect(mockUiNewPlatformSetup).toHaveBeenCalledTimes(1); expect(mockUiNewPlatformSetup).toHaveBeenCalledWith(expect.any(Object), {}); }); + + it('throws error if requireNewPlatformShimModule is undefined', () => { + const legacyPlatform = new LegacyPlatformService({ + ...defaultParams, + requireNewPlatformShimModule: undefined, + }); + + expect(() => { + legacyPlatform.setup(defaultSetupDeps); + }).toThrowErrorMatchingInlineSnapshot( + `"requireNewPlatformShimModule must be specified when rendering a legacy application"` + ); + + expect(mockUiNewPlatformSetup).not.toHaveBeenCalled(); + }); }); }); @@ -171,6 +172,21 @@ describe('#start()', () => { expect(mockUiNewPlatformStart).toHaveBeenCalledWith(expect.any(Object), {}); }); + it('throws error if requireNewPlatformShimeModule is undefined', () => { + const legacyPlatform = new LegacyPlatformService({ + ...defaultParams, + requireNewPlatformShimModule: undefined, + }); + + expect(() => { + legacyPlatform.start(defaultStartDeps); + }).toThrowErrorMatchingInlineSnapshot( + `"requireNewPlatformShimModule must be specified when rendering a legacy application"` + ); + + expect(mockUiNewPlatformStart).not.toHaveBeenCalled(); + }); + it('resolves getStartServices with core and plugin APIs', async () => { const legacyPlatform = new LegacyPlatformService({ ...defaultParams, @@ -185,67 +201,35 @@ describe('#start()', () => { expect(pluginsStart).toBe(defaultStartDeps.plugins); }); - describe('useLegacyTestHarness = false', () => { - it('passes the targetDomElement to ui/chrome', () => { - const legacyPlatform = new LegacyPlatformService({ - ...defaultParams, - }); + it('passes the targetDomElement to legacy bootstrap module', () => { + const legacyPlatform = new LegacyPlatformService({ + ...defaultParams, + }); - legacyPlatform.setup(defaultSetupDeps); - legacyPlatform.start(defaultStartDeps); + legacyPlatform.setup(defaultSetupDeps); + legacyPlatform.start(defaultStartDeps); - expect(mockUiTestHarnessBootstrap).not.toHaveBeenCalled(); - expect(mockUiChromeBootstrap).toHaveBeenCalledTimes(1); - expect(mockUiChromeBootstrap).toHaveBeenCalledWith(defaultStartDeps.targetDomElement); - }); + expect(mockUiChromeBootstrap).toHaveBeenCalledTimes(1); + expect(mockUiChromeBootstrap).toHaveBeenCalledWith(defaultStartDeps.targetDomElement); }); - describe('useLegacyTestHarness = true', () => { - it('passes the targetDomElement to ui/test_harness', () => { + describe('load order', () => { + it('loads ui/modules before ui/chrome, and both before legacy files', () => { const legacyPlatform = new LegacyPlatformService({ ...defaultParams, - useLegacyTestHarness: true, }); + expect(mockLoadOrder).toEqual([]); + legacyPlatform.setup(defaultSetupDeps); legacyPlatform.start(defaultStartDeps); - expect(mockUiChromeBootstrap).not.toHaveBeenCalled(); - expect(mockUiTestHarnessBootstrap).toHaveBeenCalledTimes(1); - expect(mockUiTestHarnessBootstrap).toHaveBeenCalledWith(defaultStartDeps.targetDomElement); - }); - }); - - describe('load order', () => { - describe('useLegacyTestHarness = false', () => { - it('loads ui/modules before ui/chrome, and both before legacy files', () => { - const legacyPlatform = new LegacyPlatformService({ - ...defaultParams, - }); - - expect(mockLoadOrder).toEqual([]); - - legacyPlatform.setup(defaultSetupDeps); - legacyPlatform.start(defaultStartDeps); - - expect(mockLoadOrder).toMatchSnapshot(); - }); - }); - - describe('useLegacyTestHarness = true', () => { - it('loads ui/modules before ui/test_harness, and both before legacy files', () => { - const legacyPlatform = new LegacyPlatformService({ - ...defaultParams, - useLegacyTestHarness: true, - }); - - expect(mockLoadOrder).toEqual([]); - - legacyPlatform.setup(defaultSetupDeps); - legacyPlatform.start(defaultStartDeps); - - expect(mockLoadOrder).toMatchSnapshot(); - }); + expect(mockLoadOrder).toMatchInlineSnapshot(` + Array [ + "ui/chrome", + "legacy files", + ] + `); }); }); }); @@ -262,7 +246,17 @@ describe('#stop()', () => { }); legacyPlatform.stop(); - expect(targetDomElement).toMatchSnapshot(); + expect(targetDomElement).toMatchInlineSnapshot(` +
+ + +

+ this should not be removed +

+ + +
+ `); }); it('destroys the angular scope and empties the targetDomElement if angular is bootstrapped to targetDomElement', async () => { @@ -291,7 +285,11 @@ describe('#stop()', () => { legacyPlatform.start({ ...defaultStartDeps, targetDomElement }); legacyPlatform.stop(); - expect(targetDomElement).toMatchSnapshot(); + expect(targetDomElement).toMatchInlineSnapshot(` +
+ `); expect(scopeDestroySpy).toHaveBeenCalledTimes(1); }); }); diff --git a/src/core/public/legacy/legacy_service.ts b/src/core/public/legacy/legacy_service.ts index 39ca7bdf54b7c..01837ba6f5940 100644 --- a/src/core/public/legacy/legacy_service.ts +++ b/src/core/public/legacy/legacy_service.ts @@ -25,8 +25,12 @@ import { LegacyCoreSetup, LegacyCoreStart, MountPoint } from '../'; /** @internal */ export interface LegacyPlatformParams { - requireLegacyFiles: () => void; - useLegacyTestHarness?: boolean; + requireLegacyFiles?: () => void; + requireLegacyBootstrapModule?: () => BootstrapModule; + requireNewPlatformShimModule?: () => { + __setup__: (legacyCore: LegacyCoreSetup, plugins: Record) => void; + __start__: (legacyCore: LegacyCoreStart, plugins: Record) => void; + }; } interface SetupDeps { @@ -92,7 +96,13 @@ export class LegacyPlatformService { // Inject parts of the new platform into parts of the legacy platform // so that legacy APIs/modules can mimic their new platform counterparts if (core.injectedMetadata.getLegacyMode()) { - require('ui/new_platform').__setup__(legacyCore, plugins); + if (!this.params.requireNewPlatformShimModule) { + throw new Error( + `requireNewPlatformShimModule must be specified when rendering a legacy application` + ); + } + + this.params.requireNewPlatformShimModule().__setup__(legacyCore, plugins); } } @@ -131,16 +141,29 @@ export class LegacyPlatformService { this.startDependencies$.next([legacyCore, plugins, {}]); + if (!this.params.requireNewPlatformShimModule) { + throw new Error( + `requireNewPlatformShimModule must be specified when rendering a legacy application` + ); + } + if (!this.params.requireLegacyBootstrapModule) { + throw new Error( + `requireLegacyBootstrapModule must be specified when rendering a legacy application` + ); + } + // Inject parts of the new platform into parts of the legacy platform // so that legacy APIs/modules can mimic their new platform counterparts - require('ui/new_platform').__start__(legacyCore, plugins); + this.params.requireNewPlatformShimModule().__start__(legacyCore, plugins); // Load the bootstrap module before loading the legacy platform files so that // the bootstrap module can modify the environment a bit first - this.bootstrapModule = this.loadBootstrapModule(); + this.bootstrapModule = this.params.requireLegacyBootstrapModule(); // require the files that will tie into the legacy platform - this.params.requireLegacyFiles(); + if (this.params.requireLegacyFiles) { + this.params.requireLegacyFiles(); + } if (!this.bootstrapModule) { throw new Error('Bootstrap module must be loaded before `start`'); @@ -172,20 +195,6 @@ export class LegacyPlatformService { // clear the inner html of the root angular element this.targetDomElement.textContent = ''; } - - private loadBootstrapModule(): BootstrapModule { - if (this.params.useLegacyTestHarness) { - // wrapped in NODE_ENV check so the `ui/test_harness` module - // is not included in the distributable - if (process.env.IS_KIBANA_DISTRIBUTABLE !== 'true') { - return require('ui/test_harness'); - } - - throw new Error('tests bundle is not available in the distributable'); - } - - return require('ui/chrome'); - } } const notSupported = (methodName: string) => (...args: any[]) => { diff --git a/src/legacy/ui/public/chrome/directives/_kbn_chrome.scss b/src/core/public/rendering/_base.scss similarity index 96% rename from src/legacy/ui/public/chrome/directives/_kbn_chrome.scss rename to src/core/public/rendering/_base.scss index b29a83848d291..ff28fc75e367d 100644 --- a/src/legacy/ui/public/chrome/directives/_kbn_chrome.scss +++ b/src/core/public/rendering/_base.scss @@ -13,7 +13,7 @@ display: flex; flex-flow: column nowrap; position: absolute; - left: $kbnGlobalNavClosedWidth; + left: 0; top: 0; right: 0; bottom: 0; diff --git a/src/core/public/rendering/_index.scss b/src/core/public/rendering/_index.scss new file mode 100644 index 0000000000000..c8567498b42ec --- /dev/null +++ b/src/core/public/rendering/_index.scss @@ -0,0 +1 @@ +@import './base'; diff --git a/src/core/public/saved_objects/saved_objects_client.ts b/src/core/public/saved_objects/saved_objects_client.ts index afc77806afb91..d26cadecb184a 100644 --- a/src/core/public/saved_objects/saved_objects_client.ts +++ b/src/core/public/saved_objects/saved_objects_client.ts @@ -28,12 +28,6 @@ import { SavedObjectsMigrationVersion, } from '../../server'; -// TODO: Migrate to an error modal powered by the NP? -import { - isAutoCreateIndexError, - showAutoCreateIndexErrorPage, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../legacy/ui/public/error_auto_create_index/error_auto_create_index'; import { SimpleSavedObject } from './simple_saved_object'; import { HttpFetchOptions, HttpSetup } from '../http'; @@ -226,7 +220,9 @@ export class SavedObjectsClient { .then(resp => this.createSavedObject(resp)) .catch((error: object) => { if (isAutoCreateIndexError(error)) { - showAutoCreateIndexErrorPage(); + window.location.assign( + this.http.basePath.prepend('/app/kibana#/error/action.auto_create_index') + ); } throw error; @@ -472,3 +468,9 @@ const renameKeys = , U extends Record ...{ [keysMap[key] || key]: obj[key] }, }; }, {}); + +const isAutoCreateIndexError = (error: any) => { + return ( + error?.res?.status === 503 && error?.body?.attributes?.code === 'ES_AUTO_CREATE_INDEX_ERROR' + ); +}; diff --git a/src/dev/build/tasks/build_kibana_platform_plugins.js b/src/dev/build/tasks/build_kibana_platform_plugins.js index 101d6bd15fc10..79cd698e4782c 100644 --- a/src/dev/build/tasks/build_kibana_platform_plugins.js +++ b/src/dev/build/tasks/build_kibana_platform_plugins.js @@ -29,6 +29,7 @@ export const BuildKibanaPlatformPluginsTask = { examples: false, watch: false, dist: true, + includeCoreBundle: true, }); await runOptimizer(optimizerConfig) diff --git a/src/legacy/core_plugins/tests_bundle/tests_entry_template.js b/src/legacy/core_plugins/tests_bundle/tests_entry_template.js index 3e3dc284671da..f075d8365c299 100644 --- a/src/legacy/core_plugins/tests_bundle/tests_entry_template.js +++ b/src/legacy/core_plugins/tests_bundle/tests_entry_template.js @@ -135,7 +135,16 @@ const coreSystem = new CoreSystem({ }, }, rootDomElement, - useLegacyTestHarness: true, + requireLegacyBootstrapModule: () => { + // wrapped in NODE_ENV check so the 'ui/test_harness' module + // is not included in the distributable + if (process.env.IS_KIBANA_DISTRIBUTABLE !== 'true') { + return require('ui/test_harness'); + } + + throw new Error('tests bundle is not available in the distributable'); + }, + requireNewPlatformShimModule: () => require('ui/new_platform'), requireLegacyFiles: () => { ${bundle.getRequires().join('\n ')} } diff --git a/src/legacy/ui/public/_index.scss b/src/legacy/ui/public/_index.scss index aaed52f8b120a..b36e62297cc23 100644 --- a/src/legacy/ui/public/_index.scss +++ b/src/legacy/ui/public/_index.scss @@ -9,7 +9,6 @@ // kbnChart__legend-isLoading @import './accessibility/index'; -@import './chrome/index'; @import './directives/index'; @import './error_auto_create_index/index'; @import './error_url_overflow/index'; diff --git a/src/legacy/ui/public/chrome/_index.scss b/src/legacy/ui/public/chrome/_index.scss deleted file mode 100644 index 7e6c3ebaccc5c..0000000000000 --- a/src/legacy/ui/public/chrome/_index.scss +++ /dev/null @@ -1,3 +0,0 @@ -@import './variables'; - -@import './directives/index'; diff --git a/src/legacy/ui/public/chrome/_variables.scss b/src/legacy/ui/public/chrome/_variables.scss deleted file mode 100644 index 5097fe4c9bfae..0000000000000 --- a/src/legacy/ui/public/chrome/_variables.scss +++ /dev/null @@ -1,4 +0,0 @@ -$kbnGlobalNavClosedWidth: 53px; -$kbnGlobalNavOpenWidth: 180px; -$kbnGlobalNavLogoHeight: 70px; -$kbnGlobalNavAppIconHeight: $euiSizeXXL + $euiSizeXS; diff --git a/src/legacy/ui/public/chrome/directives/_index.scss b/src/legacy/ui/public/chrome/directives/_index.scss deleted file mode 100644 index 4d00b02279116..0000000000000 --- a/src/legacy/ui/public/chrome/directives/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './kbn_chrome'; diff --git a/src/legacy/ui/ui_bundles/app_entry_template.js b/src/legacy/ui/ui_bundles/app_entry_template.js index a1c3a153a196c..683fedd34316f 100644 --- a/src/legacy/ui/ui_bundles/app_entry_template.js +++ b/src/legacy/ui/ui_bundles/app_entry_template.js @@ -25,6 +25,9 @@ export const appEntryTemplate = bundle => ` * * This is programmatically created and updated, do not modify * + * Any changes to this file should be kept in sync with + * src/core/public/entry_point.ts + * * context: ${bundle.getContext()} */ @@ -45,7 +48,9 @@ i18n.load(injectedMetadata.i18n.translationsUrl) browserSupportsCsp: !window.__kbnCspNotEnforced__, requireLegacyFiles: () => { ${bundle.getRequires().join('\n ')} - } + }, + requireLegacyBootstrapModule: () => require('ui/chrome'), + requireNewPlatformShimModule: () => require('ui/new_platform'), }); coreSystem diff --git a/src/legacy/ui/ui_bundles/ui_bundles_controller.js b/src/legacy/ui/ui_bundles/ui_bundles_controller.js index 7afa283af83e0..79112fd687e84 100644 --- a/src/legacy/ui/ui_bundles/ui_bundles_controller.js +++ b/src/legacy/ui/ui_bundles/ui_bundles_controller.js @@ -99,13 +99,6 @@ export class UiBundlesController { this._postLoaders = []; this._bundles = []; - // create a bundle for core-only with no modules - this.add({ - id: 'core', - modules: [], - template: appEntryTemplate, - }); - // create a bundle for each uiApp for (const uiApp of uiApps) { this.add({ diff --git a/src/legacy/ui/ui_render/bootstrap/template.js.hbs b/src/legacy/ui/ui_render/bootstrap/template.js.hbs index 1093153edbbf7..7250fa4fc9eca 100644 --- a/src/legacy/ui/ui_render/bootstrap/template.js.hbs +++ b/src/legacy/ui/ui_render/bootstrap/template.js.hbs @@ -69,26 +69,16 @@ if (window.__kbnStrictCsp__ && window.__kbnCspNotEnforced__) { } load([ - {{#each sharedJsDepFilenames}} - '{{../regularBundlePath}}/kbn-ui-shared-deps/{{this}}', - {{/each}} - '{{regularBundlePath}}/kbn-ui-shared-deps/{{sharedJsFilename}}', - '{{dllBundlePath}}/vendors_runtime.bundle.dll.js', - {{#each dllJsChunks}} + {{#each jsDependencyPaths}} '{{this}}', {{/each}} - '{{regularBundlePath}}/commons.bundle.js', - {{!-- '{{regularBundlePath}}/plugin/data/data.plugin.js', --}} - '{{regularBundlePath}}/plugin/kibanaUtils/kibanaUtils.plugin.js', - '{{regularBundlePath}}/plugin/esUiShared/esUiShared.plugin.js', - '{{regularBundlePath}}/plugin/kibanaReact/kibanaReact.plugin.js' ], function () { load([ - '{{regularBundlePath}}/{{appId}}.bundle.js', + '{{entryBundlePath}}', {{#each styleSheetPaths}} '{{this}}', {{/each}} - ]) + ]); }); - }; + } } diff --git a/src/legacy/ui/ui_render/ui_render_mixin.js b/src/legacy/ui/ui_render/ui_render_mixin.js index 0912d8683fc48..1e84405dd5153 100644 --- a/src/legacy/ui/ui_render/ui_render_mixin.js +++ b/src/legacy/ui/ui_render/ui_render_mixin.js @@ -103,41 +103,64 @@ export function uiRenderMixin(kbnServer, server, config) { const dllJsChunks = DllCompiler.getRawDllConfig().chunks.map( chunk => `${dllBundlePath}/vendors${chunk}.bundle.dll.js` ); + const styleSheetPaths = [ - ...dllStyleChunks, + ...(isCore ? [] : dllStyleChunks), `${basePath}/bundles/kbn-ui-shared-deps/${UiSharedDeps.baseCssDistFilename}`, ...(darkMode ? [ `${basePath}/bundles/kbn-ui-shared-deps/${UiSharedDeps.darkCssDistFilename}`, `${basePath}/node_modules/@kbn/ui-framework/dist/kui_dark.css`, + `${regularBundlePath}/dark_theme.style.css`, ] : [ `${basePath}/bundles/kbn-ui-shared-deps/${UiSharedDeps.lightCssDistFilename}`, `${basePath}/node_modules/@kbn/ui-framework/dist/kui_light.css`, + `${regularBundlePath}/light_theme.style.css`, ]), - `${regularBundlePath}/${darkMode ? 'dark' : 'light'}_theme.style.css`, `${regularBundlePath}/commons.style.css`, - ...(!isCore ? [`${regularBundlePath}/${app.getId()}.style.css`] : []), - ...kbnServer.uiExports.styleSheetPaths - .filter(path => path.theme === '*' || path.theme === (darkMode ? 'dark' : 'light')) - .map(path => - path.localPath.endsWith('.scss') - ? `${basePath}/built_assets/css/${path.publicPath}` - : `${basePath}/${path.publicPath}` - ) - .reverse(), + ...(isCore + ? [] + : [ + `${regularBundlePath}/${app.getId()}.style.css`, + ...kbnServer.uiExports.styleSheetPaths + .filter( + path => path.theme === '*' || path.theme === (darkMode ? 'dark' : 'light') + ) + .map(path => + path.localPath.endsWith('.scss') + ? `${basePath}/built_assets/css/${path.publicPath}` + : `${basePath}/${path.publicPath}` + ) + .reverse(), + ]), + ]; + + const jsDependencyPaths = [ + ...UiSharedDeps.jsDepFilenames.map( + filename => `${regularBundlePath}/kbn-ui-shared-deps/${filename}` + ), + `${regularBundlePath}/kbn-ui-shared-deps/${UiSharedDeps.jsFilename}`, + ...(isCore + ? [] + : [ + `${dllBundlePath}/vendors_runtime.bundle.dll.js`, + ...dllJsChunks, + `${regularBundlePath}/commons.bundle.js`, + ]), + `${regularBundlePath}/plugin/kibanaUtils/kibanaUtils.plugin.js`, + `${regularBundlePath}/plugin/esUiShared/esUiShared.plugin.js`, + `${regularBundlePath}/plugin/kibanaReact/kibanaReact.plugin.js`, ]; const bootstrap = new AppBootstrap({ templateData: { - appId: isCore ? 'core' : app.getId(), - regularBundlePath, - dllBundlePath, - dllJsChunks, - styleSheetPaths, - sharedJsFilename: UiSharedDeps.jsFilename, - sharedJsDepFilenames: UiSharedDeps.jsDepFilenames, darkMode, + jsDependencyPaths, + styleSheetPaths, + entryBundlePath: isCore + ? `${regularBundlePath}/core/core.entry.js` + : `${regularBundlePath}/${app.getId()}.bundle.js`, }, }); diff --git a/src/optimize/bundles_route/bundles_route.js b/src/optimize/bundles_route/bundles_route.js index 0c2e98b5acd63..530dabb9a46a3 100644 --- a/src/optimize/bundles_route/bundles_route.js +++ b/src/optimize/bundles_route/bundles_route.js @@ -17,11 +17,12 @@ * under the License. */ -import { isAbsolute, extname } from 'path'; +import { isAbsolute, extname, join } from 'path'; import LruCache from 'lru-cache'; import * as UiSharedDeps from '@kbn/ui-shared-deps'; import { createDynamicAssetResponse } from './dynamic_asset_response'; import { assertIsNpUiPluginPublicDirs } from '../np_ui_plugin_public_dirs'; +import { fromRoot } from '../../core/server/utils'; /** * Creates the routes that serves files from `bundlesPath` or from @@ -85,6 +86,12 @@ export function createBundlesRoute({ fileHashCache ) ), + buildRouteForBundles( + `${basePublicPath}/bundles/core/`, + `/bundles/core/`, + fromRoot(join('src', 'core', 'target', 'public')), + fileHashCache + ), buildRouteForBundles( `${basePublicPath}/bundles/`, '/bundles/', diff --git a/x-pack/plugins/graph/public/angular/templates/_graph.scss b/x-pack/plugins/graph/public/angular/templates/_graph.scss index e6bd4693d1e9b..4ba65e7ec6b96 100644 --- a/x-pack/plugins/graph/public/angular/templates/_graph.scss +++ b/x-pack/plugins/graph/public/angular/templates/_graph.scss @@ -1,5 +1,3 @@ -@import 'src/legacy/ui/public/chrome/variables'; - @mixin gphSvgText() { font-family: $euiFontFamily; font-size: $euiSizeS; diff --git a/x-pack/plugins/graph/public/application.ts b/x-pack/plugins/graph/public/application.ts index f804265f1f5ab..fee42bdbeaf3b 100644 --- a/x-pack/plugins/graph/public/application.ts +++ b/x-pack/plugins/graph/public/application.ts @@ -12,6 +12,8 @@ import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; import '../../../../webpackShims/ace'; // required for i18nIdDirective import 'angular-sanitize'; +// required for ngRoute +import 'angular-route'; // type imports import { AppMountContext, diff --git a/x-pack/plugins/graph/public/index.scss b/x-pack/plugins/graph/public/index.scss index f4e38de3e93a4..964ef320e4352 100644 --- a/x-pack/plugins/graph/public/index.scss +++ b/x-pack/plugins/graph/public/index.scss @@ -12,3 +12,5 @@ @import './main'; @import './angular/templates/index'; @import './components/index'; +// Local application mount wrapper styles +@import 'src/legacy/core_plugins/kibana/public/local_application_service/index'; From cd054285c8d1228f4026e6a990a8d2028961b229 Mon Sep 17 00:00:00 2001 From: Corey Robertson Date: Wed, 22 Apr 2020 14:02:19 -0400 Subject: [PATCH 06/39] [CANVAS] Pull UI Settings from Kibana Platform (#63397) * Pull UI Settings from Kibana Platform * Typecheck * Fix type check Co-authored-by: Elastic Machine --- .../canvas_plugin_src/elements/index.ts | 21 ++++-- .../elements/metric/index.ts | 39 ++++++----- .../functions/common/timefilterControl.ts | 2 +- .../canvas/canvas_plugin_src/plugin.ts | 17 ++--- .../canvas_plugin_src/renderers/index.js | 5 +- .../time_filter/components/index.tsx | 20 +----- .../renderers/time_filter/index.js | 47 ------------- .../renderers/time_filter/index.tsx | 70 +++++++++++++++++++ .../uis/arguments/date_format/index.ts | 52 +++++++------- .../uis/arguments/{index.js => index.ts} | 26 +++++-- .../uis/arguments/number_format/index.ts | 44 ++++++------ .../uis/views/{index.js => index.ts} | 25 ++++++- .../canvas_plugin_src/uis/views/metric.js | 48 ------------- .../canvas_plugin_src/uis/views/metric.ts | 50 +++++++++++++ .../i18n/elements/element_strings.test.ts | 6 +- .../public/components/workpad_loader/index.js | 13 +++- .../workpad_loader/workpad_loader.js | 9 +-- .../public/lib/kibana_advanced_settings.ts | 9 --- 18 files changed, 285 insertions(+), 218 deletions(-) delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/time_filter/index.js create mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/time_filter/index.tsx rename x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/{index.js => index.ts} (55%) rename x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/{index.js => index.ts} (56%) delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/metric.js create mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/metric.ts delete mode 100644 x-pack/legacy/plugins/canvas/public/lib/kibana_advanced_settings.ts diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/index.ts index feba88dbe8b90..6c0cd7eb33dc0 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/index.ts @@ -16,7 +16,7 @@ import { horizontalProgressPill } from './horizontal_progress_pill'; import { image } from './image'; import { lineChart } from './line_chart'; import { markdown } from './markdown'; -import { metric } from './metric'; +import { metricElementInitializer } from './metric'; import { pie } from './pie'; import { plot } from './plot'; import { progressGauge } from './progress_gauge'; @@ -32,7 +32,10 @@ import { verticalBarChart } from './vert_bar_chart'; import { verticalProgressBar } from './vertical_progress_bar'; import { verticalProgressPill } from './vertical_progress_pill'; -export const elementSpecs = applyElementStrings([ +import { SetupInitializer } from '../plugin'; +import { ElementFactory } from '../../types'; + +const elementSpecs = [ areaChart, bubbleChart, debug, @@ -44,7 +47,6 @@ export const elementSpecs = applyElementStrings([ horizontalProgressPill, lineChart, markdown, - metric, pie, plot, progressGauge, @@ -59,4 +61,15 @@ export const elementSpecs = applyElementStrings([ verticalBarChart, verticalProgressBar, verticalProgressPill, -]); +]; + +const initializeElementFactories = [metricElementInitializer]; + +export const initializeElements: SetupInitializer = (core, plugins) => { + const specs = [ + ...elementSpecs, + ...initializeElementFactories.map(factory => factory(core, plugins)), + ]; + + return applyElementStrings(specs); +}; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/metric/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/metric/index.ts index c08c090f11f91..7e9aca9c01782 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/metric/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/metric/index.ts @@ -6,23 +6,26 @@ import { openSans } from '../../../common/lib/fonts'; import header from './header.png'; -import { getAdvancedSettings } from '../../../public/lib/kibana_advanced_settings'; import { ElementFactory } from '../../../types'; -export const metric: ElementFactory = () => ({ - name: 'metric', - displayName: 'Metric', - tags: ['text'], - help: 'A number with a label', - width: 200, - height: 100, - image: header, - expression: `filters -| demodata -| math "unique(country)" -| metric "Countries" - metricFont={font size=48 family="${openSans.value}" color="#000000" align="center" lHeight=48} - labelFont={font size=14 family="${openSans.value}" color="#000000" align="center"} - metricFormat="${getAdvancedSettings().get('format:number:defaultPattern')}" -| render`, -}); +import { SetupInitializer } from '../../plugin'; + +export const metricElementInitializer: SetupInitializer = (core, setup) => { + return () => ({ + name: 'metric', + displayName: 'Metric', + tags: ['text'], + help: 'A number with a label', + width: 200, + height: 100, + image: header, + expression: `filters + | demodata + | math "unique(country)" + | metric "Countries" + metricFont={font size=48 family="${openSans.value}" color="#000000" align="center" lHeight=48} + labelFont={font size=14 family="${openSans.value}" color="#000000" align="center"} + metricFormat="${core.uiSettings.get('format:number:defaultPattern')}" + | render`, + }); +}; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/timefilterControl.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/timefilterControl.ts index 2f7f5c26ad0b7..61ecd66455e78 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/timefilterControl.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/timefilterControl.ts @@ -8,7 +8,7 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { Render } from '../../../types'; import { getFunctionHelp } from '../../../i18n'; -interface Arguments { +export interface Arguments { column: string; compact: boolean; filterGroup: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/plugin.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/plugin.ts index a654c6b28b350..4452e5e9e31fe 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/plugin.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/plugin.ts @@ -14,18 +14,16 @@ import { functions } from './functions/browser'; import { typeFunctions } from './expression_types'; // @ts-ignore: untyped local import { renderFunctions, renderFunctionFactories } from './renderers'; - -import { elementSpecs } from './elements'; +import { initializeElements } from './elements'; // @ts-ignore Untyped Local import { transformSpecs } from './uis/transforms'; // @ts-ignore Untyped Local import { datasourceSpecs } from './uis/datasources'; // @ts-ignore Untyped Local import { modelSpecs } from './uis/models'; +import { initializeViews } from './uis/views'; // @ts-ignore Untyped Local -import { viewSpecs } from './uis/views'; -// @ts-ignore Untyped Local -import { args as argSpecs } from './uis/arguments'; +import { initializeArgs } from './uis/arguments'; import { tagSpecs } from './uis/tags'; import { templateSpecs } from './templates'; @@ -39,6 +37,9 @@ export interface StartDeps { inspector: InspectorStart; } +export type SetupInitializer = (core: CoreSetup, plugins: SetupDeps) => T; +export type StartInitializer = (core: CoreStart, plugins: StartDeps) => T; + /** @internal */ export class CanvasSrcPlugin implements Plugin { public setup(core: CoreSetup, plugins: SetupDeps) { @@ -53,11 +54,11 @@ export class CanvasSrcPlugin implements Plugin ); }); - plugins.canvas.addElements(elementSpecs); + plugins.canvas.addElements(initializeElements(core, plugins)); plugins.canvas.addDatasourceUIs(datasourceSpecs); plugins.canvas.addModelUIs(modelSpecs); - plugins.canvas.addViewUIs(viewSpecs); - plugins.canvas.addArgumentUIs(argSpecs); + plugins.canvas.addViewUIs(initializeViews(core, plugins)); + plugins.canvas.addArgumentUIs(initializeArgs(core, plugins)); plugins.canvas.addTagUIs(tagSpecs); plugins.canvas.addTemplates(templateSpecs); plugins.canvas.addTransformUIs(transformSpecs); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/index.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/index.js index 84f92f5149893..263f2d8ec30b5 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/index.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/index.js @@ -20,7 +20,7 @@ import { revealImage } from './reveal_image'; import { shape } from './shape'; import { table } from './table'; import { text } from './text'; -import { timeFilter } from './time_filter'; +import { timeFilterFactory } from './time_filter'; export const renderFunctions = [ advancedFilter, @@ -38,7 +38,6 @@ export const renderFunctions = [ shape, table, text, - timeFilter, ]; -export const renderFunctionFactories = [embeddableRendererFactory]; +export const renderFunctionFactories = [embeddableRendererFactory, timeFilterFactory]; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/time_filter/components/index.tsx b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/time_filter/components/index.tsx index 55a453720e2f0..85ea754de670d 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/time_filter/components/index.tsx +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/time_filter/components/index.tsx @@ -4,22 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { getAdvancedSettings } from '../../../../public/lib/kibana_advanced_settings'; -import { TimeFilter as Component, Props } from './time_filter'; +import { TimeFilter } from './time_filter'; -export const TimeFilter = (props: Props) => { - const customQuickRanges = (getAdvancedSettings().get('timepicker:quickRanges') || []).map( - ({ from, to, display }: { from: string; to: string; display: string }) => ({ - start: from, - end: to, - label: display, - }) - ); - - const customDateFormat = getAdvancedSettings().get('dateFormat'); - - return ( - - ); -}; +export { TimeFilter }; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/time_filter/index.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/time_filter/index.js deleted file mode 100644 index cbc514e218d74..0000000000000 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/time_filter/index.js +++ /dev/null @@ -1,47 +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 ReactDOM from 'react-dom'; -import React from 'react'; -import { toExpression } from '@kbn/interpreter/common'; -import { syncFilterExpression } from '../../../public/lib/sync_filter_expression'; -import { RendererStrings } from '../../../i18n'; -import { TimeFilter } from './components'; - -const { timeFilter: strings } = RendererStrings; - -export const timeFilter = () => ({ - name: 'time_filter', - displayName: strings.getDisplayName(), - help: strings.getHelpDescription(), - reuseDomNode: true, // must be true, otherwise popovers don't work - render(domNode, config, handlers) { - const filterExpression = handlers.getFilter(); - - if (filterExpression !== '') { - // NOTE: setFilter() will cause a data refresh, avoid calling unless required - // compare expression and filter, update filter if needed - const { changed, newAst } = syncFilterExpression(config, filterExpression, [ - 'column', - 'filterGroup', - ]); - - if (changed) { - handlers.setFilter(toExpression(newAst)); - } - } - - ReactDOM.render( - , - domNode, - () => handlers.done() - ); - - handlers.onDestroy(() => { - ReactDOM.unmountComponentAtNode(domNode); - }); - }, -}); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/time_filter/index.tsx b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/time_filter/index.tsx new file mode 100644 index 0000000000000..ef5bfb70d4b3d --- /dev/null +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/time_filter/index.tsx @@ -0,0 +1,70 @@ +/* + * 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 ReactDOM from 'react-dom'; +import React from 'react'; +import { toExpression } from '@kbn/interpreter/common'; +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'; + +const { timeFilter: strings } = RendererStrings; + +export const timeFilterFactory: StartInitializer> = (core, plugins) => { + const { uiSettings } = core; + + const customQuickRanges = (uiSettings.get('timepicker:quickRanges') || []).map( + ({ from, to, display }: { from: string; to: string; display: string }) => ({ + start: from, + end: to, + label: display, + }) + ); + + const customDateFormat = uiSettings.get('dateFormat'); + + return () => ({ + name: 'time_filter', + displayName: strings.getDisplayName(), + help: strings.getHelpDescription(), + reuseDomNode: true, // must be true, otherwise popovers don't work + render: async (domNode: HTMLElement, config: Arguments, handlers: RendererHandlers) => { + const filterExpression = handlers.getFilter(); + + if (filterExpression !== '') { + // NOTE: setFilter() will cause a data refresh, avoid calling unless required + // compare expression and filter, update filter if needed + const { changed, newAst } = syncFilterExpression(config, filterExpression, [ + 'column', + 'filterGroup', + ]); + + if (changed) { + handlers.setFilter(toExpression(newAst)); + } + } + + ReactDOM.render( + , + domNode, + () => handlers.done() + ); + + handlers.onDestroy(() => { + ReactDOM.unmountComponentAtNode(domNode); + }); + }, + }); +}; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/date_format/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/date_format/index.ts index d19bfa64bae76..655a362fe6d33 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/date_format/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/date_format/index.ts @@ -7,38 +7,40 @@ import { compose, withProps } from 'recompose'; import moment from 'moment'; import { DateFormatArgInput as Component, Props as ComponentProps } from './date_format'; -import { getAdvancedSettings } from '../../../../public/lib/kibana_advanced_settings'; // @ts-ignore untyped local lib import { templateFromReactComponent } from '../../../../public/lib/template_from_react_component'; import { ArgumentFactory } from '../../../../types/arguments'; import { ArgumentStrings } from '../../../../i18n'; -const { DateFormat: strings } = ArgumentStrings; +import { SetupInitializer } from '../../../plugin'; -const getFormatMap = () => ({ - DEFAULT: getAdvancedSettings().get('dateFormat'), - NANOS: getAdvancedSettings().get('dateNanosFormat'), - ISO8601: '', - LOCAL_LONG: 'LLLL', - LOCAL_SHORT: 'LLL', - LOCAL_DATE: 'l', - LOCAL_TIME_WITH_SECONDS: 'LTS', -}); +const { DateFormat: strings } = ArgumentStrings; -const now = moment(); +export const dateFormatInitializer: SetupInitializer> = ( + core, + plugins +) => { + const formatMap = { + DEFAULT: core.uiSettings.get('dateFormat'), + NANOS: core.uiSettings.get('dateNanosFormat'), + ISO8601: '', + LOCAL_LONG: 'LLLL', + LOCAL_SHORT: 'LLL', + LOCAL_DATE: 'l', + LOCAL_TIME_WITH_SECONDS: 'LTS', + }; -const dateFormats = Object.values(getFormatMap()).map(format => ({ - value: format, - text: moment.utc(now).format(format), -})); + const dateFormats = Object.values(formatMap).map(format => ({ + value: format, + text: moment.utc(moment()).format(format), + })); -export const DateFormatArgInput = compose(withProps({ dateFormats }))( - Component -); + const DateFormatArgInput = compose(withProps({ dateFormats }))(Component); -export const dateFormat: ArgumentFactory = () => ({ - name: 'dateFormat', - displayName: strings.getDisplayName(), - help: strings.getHelp(), - simpleTemplate: templateFromReactComponent(DateFormatArgInput), -}); + return () => ({ + name: 'dateFormat', + displayName: strings.getDisplayName(), + help: strings.getHelp(), + simpleTemplate: templateFromReactComponent(DateFormatArgInput), + }); +}; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/index.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/index.ts similarity index 55% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/index.js rename to x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/index.ts index 829fe3e3bef3d..bd84a2eb97d24 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/index.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/index.ts @@ -5,29 +5,41 @@ */ import { axisConfig } from './axis_config'; +// @ts-ignore untyped local import { datacolumn } from './datacolumn'; -import { dateFormat } from './date_format'; +import { dateFormatInitializer } from './date_format'; +// @ts-ignore untyped local import { filterGroup } from './filter_group'; +// @ts-ignore untyped local import { imageUpload } from './image_upload'; +// @ts-ignore untyped local import { number } from './number'; -import { numberFormat } from './number_format'; +import { numberFormatInitializer } from './number_format'; +// @ts-ignore untyped local import { palette } from './palette'; +// @ts-ignore untyped local import { percentage } from './percentage'; +// @ts-ignore untyped local import { range } from './range'; +// @ts-ignore untyped local import { select } from './select'; +// @ts-ignore untyped local import { shape } from './shape'; +// @ts-ignore untyped local import { string } from './string'; +// @ts-ignore untyped local import { textarea } from './textarea'; +// @ts-ignore untyped local import { toggle } from './toggle'; +import { SetupInitializer } from '../../plugin'; + export const args = [ axisConfig, datacolumn, - dateFormat, filterGroup, imageUpload, number, - numberFormat, palette, percentage, range, @@ -37,3 +49,9 @@ export const args = [ textarea, toggle, ]; + +export const initializers = [dateFormatInitializer, numberFormatInitializer]; + +export const initializeArgs: SetupInitializer = (core, plugins) => { + return [...args, ...initializers.map(initializer => initializer(core, plugins))]; +}; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/index.ts index ce6c90c89a5a0..4025d4deaf997 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/index.ts @@ -6,40 +6,42 @@ import { compose, withProps } from 'recompose'; import { NumberFormatArgInput as Component, Props as ComponentProps } from './number_format'; -import { getAdvancedSettings } from '../../../../public/lib/kibana_advanced_settings'; // @ts-ignore untyped local lib import { templateFromReactComponent } from '../../../../public/lib/template_from_react_component'; import { ArgumentFactory } from '../../../../types/arguments'; import { ArgumentStrings } from '../../../../i18n'; +import { SetupInitializer } from '../../../plugin'; const { NumberFormat: strings } = ArgumentStrings; -const getFormatMap = () => ({ - NUMBER: getAdvancedSettings().get('format:number:defaultPattern'), - PERCENT: getAdvancedSettings().get('format:percent:defaultPattern'), - CURRENCY: getAdvancedSettings().get('format:currency:defaultPattern'), - DURATION: '00:00:00', - BYTES: getAdvancedSettings().get('format:bytes:defaultPattern'), -}); +export const numberFormatInitializer: SetupInitializer> = ( + core, + plugins +) => { + const formatMap = { + NUMBER: core.uiSettings.get('format:number:defaultPattern'), + PERCENT: core.uiSettings.get('format:percent:defaultPattern'), + CURRENCY: core.uiSettings.get('format:currency:defaultPattern'), + DURATION: '00:00:00', + BYTES: core.uiSettings.get('format:bytes:defaultPattern'), + }; -const getNumberFormats = () => { - const formatMap = getFormatMap(); - return [ + const numberFormats = [ { value: formatMap.NUMBER, text: strings.getFormatNumber() }, { value: formatMap.PERCENT, text: strings.getFormatPercent() }, { value: formatMap.CURRENCY, text: strings.getFormatCurrency() }, { value: formatMap.DURATION, text: strings.getFormatDuration() }, { value: formatMap.BYTES, text: strings.getFormatBytes() }, ]; -}; -export const NumberFormatArgInput = compose( - withProps({ numberFormats: getNumberFormats() }) -)(Component); + const NumberFormatArgInput = compose(withProps({ numberFormats }))( + Component + ); -export const numberFormat: ArgumentFactory = () => ({ - name: 'numberFormat', - displayName: strings.getDisplayName(), - help: strings.getHelp(), - simpleTemplate: templateFromReactComponent(NumberFormatArgInput), -}); + return () => ({ + name: 'numberFormat', + displayName: strings.getDisplayName(), + help: strings.getHelp(), + simpleTemplate: templateFromReactComponent(NumberFormatArgInput), + }); +}; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/index.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/index.ts similarity index 56% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/index.js rename to x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/index.ts index 6e2685dcb9893..fdcaf21982050 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/index.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/index.ts @@ -4,27 +4,42 @@ * you may not use this file except in compliance with the Elastic License. */ +// @ts-ignore untyped local import { dropdownControl } from './dropdownControl'; +// @ts-ignore untyped local import { getCell } from './getCell'; +// @ts-ignore untyped local import { image } from './image'; +// @ts-ignore untyped local import { markdown } from './markdown'; -import { metric } from './metric'; +// @ts-ignore untyped local +import { metricInitializer } from './metric'; +// @ts-ignore untyped local import { pie } from './pie'; +// @ts-ignore untyped local import { plot } from './plot'; +// @ts-ignore untyped local import { progress } from './progress'; +// @ts-ignore untyped local import { repeatImage } from './repeatImage'; +// @ts-ignore untyped local import { revealImage } from './revealImage'; +// @ts-ignore untyped local import { render } from './render'; +// @ts-ignore untyped local import { shape } from './shape'; +// @ts-ignore untyped local import { table } from './table'; +// @ts-ignore untyped local import { timefilterControl } from './timefilterControl'; +import { SetupInitializer } from '../../plugin'; + export const viewSpecs = [ dropdownControl, getCell, image, markdown, - metric, pie, plot, progress, @@ -35,3 +50,9 @@ export const viewSpecs = [ table, timefilterControl, ]; + +export const viewInitializers = [metricInitializer]; + +export const initializeViews: SetupInitializer = (core, plugins) => { + return [...viewSpecs, ...viewInitializers.map(initializer => initializer(core, plugins))]; +}; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/metric.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/metric.js deleted file mode 100644 index e69f8f1de5952..0000000000000 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/metric.js +++ /dev/null @@ -1,48 +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 { openSans } from '../../../common/lib/fonts'; -import { getAdvancedSettings } from '../../../public/lib/kibana_advanced_settings'; -import { ViewStrings } from '../../../i18n'; - -const { Metric: strings } = ViewStrings; - -export const metric = () => ({ - name: 'metric', - displayName: strings.getDisplayName(), - modelArgs: [['_', { label: strings.getNumberDisplayName() }]], - requiresContext: false, - args: [ - { - name: 'metricFormat', - displayName: strings.getMetricFormatDisplayName(), - help: strings.getMetricFormatHelp(), - argType: 'numberFormat', - default: `"${getAdvancedSettings().get('format:number:defaultPattern')}"`, - }, - { - name: '_', - displayName: strings.getLabelDisplayName(), - help: strings.getLabelHelp(), - argType: 'string', - default: '""', - }, - { - name: 'metricFont', - displayName: strings.getMetricFontDisplayName(), - help: strings.getMetricFontHelp(), - argType: 'font', - default: `{font size=48 family="${openSans.value}" color="#000000" align=center lHeight=48}`, - }, - { - name: 'labelFont', - displayName: strings.getLabelFontDisplayName(), - help: strings.getLabelFontHelp(), - argType: 'font', - default: `{font size=18 family="${openSans.value}" color="#000000" align=center}`, - }, - ], -}); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/metric.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/metric.ts new file mode 100644 index 0000000000000..93912b7b0517f --- /dev/null +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/metric.ts @@ -0,0 +1,50 @@ +/* + * 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 { openSans } from '../../../common/lib/fonts'; +import { ViewStrings } from '../../../i18n'; +import { SetupInitializer } from '../../plugin'; + +const { Metric: strings } = ViewStrings; + +export const metricInitializer: SetupInitializer = (core, plugin) => { + return () => ({ + name: 'metric', + displayName: strings.getDisplayName(), + modelArgs: [['_', { label: strings.getNumberDisplayName() }]], + requiresContext: false, + args: [ + { + name: 'metricFormat', + displayName: strings.getMetricFormatDisplayName(), + help: strings.getMetricFormatHelp(), + argType: 'numberFormat', + default: `"${core.uiSettings.get('format:number:defaultPattern')}"`, + }, + { + name: '_', + displayName: strings.getLabelDisplayName(), + help: strings.getLabelHelp(), + argType: 'string', + default: '""', + }, + { + name: 'metricFont', + displayName: strings.getMetricFontDisplayName(), + help: strings.getMetricFontHelp(), + argType: 'font', + default: `{font size=48 family="${openSans.value}" color="#000000" align=center lHeight=48}`, + }, + { + name: 'labelFont', + displayName: strings.getLabelFontDisplayName(), + help: strings.getLabelFontHelp(), + argType: 'font', + default: `{font size=18 family="${openSans.value}" color="#000000" align=center}`, + }, + ], + }); +}; diff --git a/x-pack/legacy/plugins/canvas/i18n/elements/element_strings.test.ts b/x-pack/legacy/plugins/canvas/i18n/elements/element_strings.test.ts index 3d835bdf31bf8..ce908ac6c5566 100644 --- a/x-pack/legacy/plugins/canvas/i18n/elements/element_strings.test.ts +++ b/x-pack/legacy/plugins/canvas/i18n/elements/element_strings.test.ts @@ -3,9 +3,11 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -jest.mock('ui/new_platform'); import { getElementStrings } from './element_strings'; -import { elementSpecs } from '../../canvas_plugin_src/elements'; +import { initializeElements } from '../../canvas_plugin_src/elements'; +import { coreMock } from '../../../../../../src/core/public/mocks'; + +const elementSpecs = initializeElements(coreMock.createSetup() as any, {} as any); import { TagStrings } from '../tags'; diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_loader/index.js b/x-pack/legacy/plugins/canvas/public/components/workpad_loader/index.js index 429d27afb3f0d..226ad420535bd 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_loader/index.js +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_loader/index.js @@ -6,7 +6,8 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import { compose, withState, getContext, withHandlers } from 'recompose'; +import { compose, withState, getContext, withHandlers, withProps } from 'recompose'; +import moment from 'moment'; import * as workpadService from '../../lib/workpad_service'; import { notify } from '../../lib/notify'; import { canUserWrite } from '../../state/selectors/app'; @@ -14,6 +15,7 @@ import { getWorkpad } from '../../state/selectors/workpad'; import { getId } from '../../lib/get_id'; import { downloadWorkpad } from '../../lib/download_workpad'; import { ComponentStrings, ErrorStrings } from '../../../i18n'; +import { withKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { WorkpadLoader as Component } from './workpad_loader'; const { WorkpadLoader: strings } = ComponentStrings; @@ -127,5 +129,12 @@ export const WorkpadLoader = compose( return errors.map(({ id }) => id); }); }, - }) + }), + withKibana, + withProps(props => ({ + formatDate: date => { + const dateFormat = props.kibana.services.uiSettings.get('dateFormat'); + return date && moment(date).format(dateFormat); + }, + })) )(Component); diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_loader/workpad_loader.js b/x-pack/legacy/plugins/canvas/public/components/workpad_loader/workpad_loader.js index 30d4ded8571c5..04378e5603c4b 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_loader/workpad_loader.js +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_loader/workpad_loader.js @@ -20,12 +20,10 @@ import { EuiLink, } from '@elastic/eui'; import { sortByOrder } from 'lodash'; -import moment from 'moment'; import { ConfirmModal } from '../confirm_modal'; import { Link } from '../link'; import { Paginate } from '../paginate'; import { ComponentStrings } from '../../../i18n'; -import { getAdvancedSettings } from '../../lib/kibana_advanced_settings'; import { WorkpadDropzone } from './workpad_dropzone'; import { WorkpadCreate } from './workpad_create'; import { WorkpadSearch } from './workpad_search'; @@ -33,8 +31,6 @@ import { uploadWorkpad } from './upload_workpad'; const { WorkpadLoader: strings } = ComponentStrings; -const formatDate = date => date && moment(date).format(getAdvancedSettings().get('dateFormat')); - const getDisplayName = (name, workpad, loadedWorkpad) => { const workpadName = name.length ? name : {workpad.id}; return workpad.id === loadedWorkpad ? {workpadName} : workpadName; @@ -51,6 +47,7 @@ export class WorkpadLoader extends React.PureComponent { removeWorkpads: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired, workpads: PropTypes.object, + formatDate: PropTypes.func.isRequired, }; state = { @@ -205,7 +202,7 @@ export class WorkpadLoader extends React.PureComponent { sortable: true, dataType: 'date', width: '20%', - render: date => formatDate(date), + render: date => this.props.formatDate(date), }, { field: '@timestamp', @@ -213,7 +210,7 @@ export class WorkpadLoader extends React.PureComponent { sortable: true, dataType: 'date', width: '20%', - render: date => formatDate(date), + render: date => this.props.formatDate(date), }, { name: '', actions, width: '5%' }, ]; diff --git a/x-pack/legacy/plugins/canvas/public/lib/kibana_advanced_settings.ts b/x-pack/legacy/plugins/canvas/public/lib/kibana_advanced_settings.ts deleted file mode 100644 index f57f3188a8184..0000000000000 --- a/x-pack/legacy/plugins/canvas/public/lib/kibana_advanced_settings.ts +++ /dev/null @@ -1,9 +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 { getCoreStart } from '../legacy'; - -export const getAdvancedSettings = () => getCoreStart().uiSettings; From 12aae67467348be07124bd797fd7be6b5a9199f0 Mon Sep 17 00:00:00 2001 From: Zacqary Adam Xeper Date: Wed, 22 Apr 2020 13:09:09 -0500 Subject: [PATCH 07/39] [Metrics Alerts] Add outside range comparator (#63993) --- .../alerting/metrics/expression.tsx | 41 +++++++++++-------- .../alerting/metrics/validation.tsx | 9 ++-- .../metric_threshold_executor.test.ts | 8 ++++ .../metric_threshold_executor.ts | 15 +++---- .../register_metric_threshold_alert_type.ts | 25 +++++------ .../lib/alerting/metric_threshold/types.ts | 15 +++++-- 6 files changed, 67 insertions(+), 46 deletions(-) diff --git a/x-pack/plugins/infra/public/components/alerting/metrics/expression.tsx b/x-pack/plugins/infra/public/components/alerting/metrics/expression.tsx index 665da25dcbfff..cdefffeb35c15 100644 --- a/x-pack/plugins/infra/public/components/alerting/metrics/expression.tsx +++ b/x-pack/plugins/infra/public/components/alerting/metrics/expression.tsx @@ -20,6 +20,7 @@ import { i18n } from '@kbn/i18n'; import { MetricExpressionParams, Comparator, + Aggregators, // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../../server/lib/alerting/metric_threshold/types'; import { euiStyled } from '../../../../../observability/public'; @@ -31,6 +32,8 @@ import { // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../../../triggers_actions_ui/public/common'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { builtInComparators } from '../../../../../triggers_actions_ui/public/common/constants'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths import { IErrorObject } from '../../../../../triggers_actions_ui/public/types'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AlertsContextValue } from '../../../../../triggers_actions_ui/public/application/context/alerts_context'; @@ -64,24 +67,25 @@ type MetricExpression = Omit & { metric?: string; }; -enum AGGREGATION_TYPES { - COUNT = 'count', - AVERAGE = 'avg', - SUM = 'sum', - MIN = 'min', - MAX = 'max', - RATE = 'rate', - CARDINALITY = 'cardinality', -} - const defaultExpression = { - aggType: AGGREGATION_TYPES.AVERAGE, + aggType: Aggregators.AVERAGE, comparator: Comparator.GT, threshold: [], timeSize: 1, timeUnit: 'm', } as MetricExpression; +const customComparators = { + ...builtInComparators, + [Comparator.OUTSIDE_RANGE]: { + text: i18n.translate('xpack.infra.metrics.alertFlyout.outsideRangeLabel', { + defaultMessage: 'Is not between', + }), + value: Comparator.OUTSIDE_RANGE, + requiredValues: 2, + }, +}; + export const Expressions: React.FC = props => { const { setAlertParams, alertParams, errors, alertsContext } = props; const { source, createDerivedIndexPattern } = useSourceViaHttp({ @@ -339,7 +343,7 @@ const StyledExpression = euiStyled.div` export const ExpressionRow: React.FC = props => { const { setAlertParams, expression, errors, expressionId, remove, fields, canDelete } = props; const { - aggType = AGGREGATION_TYPES.MAX, + aggType = Aggregators.MAX, metric, comparator = Comparator.GT, threshold = [], @@ -410,6 +414,7 @@ export const ExpressionRow: React.FC = props => { { expect(mostRecentAction(instanceID)).toBe(undefined); expect(getState(instanceID).alertState).toBe(AlertStates.OK); }); + test('alerts as expected with the outside range comparator', async () => { + await execute(Comparator.OUTSIDE_RANGE, [0, 0.75]); + expect(mostRecentAction(instanceID).id).toBe(FIRED_ACTIONS.id); + expect(getState(instanceID).alertState).toBe(AlertStates.ALERT); + await execute(Comparator.OUTSIDE_RANGE, [0, 1.5]); + expect(mostRecentAction(instanceID)).toBe(undefined); + expect(getState(instanceID).alertState).toBe(AlertStates.OK); + }); test('reports expected values to the action context', async () => { await execute(Comparator.GT, [0.75]); const { action } = mostRecentAction(instanceID); diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts index c5ea65f7a4d1a..946f1c14bf593 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts @@ -11,7 +11,7 @@ import { InfraDatabaseSearchResponse } from '../../adapters/framework/adapter_ty import { createAfterKeyHandler } from '../../../utils/create_afterkey_handler'; import { getAllCompositeData } from '../../../utils/get_all_composite_data'; import { networkTraffic } from '../../../../common/inventory_models/shared/metrics/snapshot/network_traffic'; -import { MetricExpressionParams, Comparator, AlertStates } from './types'; +import { MetricExpressionParams, Comparator, Aggregators, AlertStates } from './types'; import { AlertServices, AlertExecutorOptions } from '../../../../../alerting/server'; import { getIntervalInSeconds } from '../../../utils/get_interval_in_seconds'; import { getDateHistogramOffset } from '../../snapshot/query_helpers'; @@ -39,7 +39,7 @@ const getCurrentValueFromAggregations = ( const { buckets } = aggregations.aggregatedIntervals; if (!buckets.length) return null; // No Data state const mostRecentBucket = buckets[buckets.length - 1]; - if (aggType === 'count') { + if (aggType === Aggregators.COUNT) { return mostRecentBucket.doc_count; } const { value } = mostRecentBucket.aggregatedValue; @@ -70,10 +70,10 @@ export const getElasticsearchMetricQuery = ( groupBy?: string, filterQuery?: string ) => { - if (aggType === 'count' && metric) { + if (aggType === Aggregators.COUNT && metric) { throw new Error('Cannot aggregate document count with a metric'); } - if (aggType !== 'count' && !metric) { + if (aggType !== Aggregators.COUNT && !metric) { throw new Error('Can only aggregate without a metric if using the document count aggregator'); } const interval = `${timeSize}${timeUnit}`; @@ -85,9 +85,9 @@ export const getElasticsearchMetricQuery = ( const offset = getDateHistogramOffset(from, interval); const aggregations = - aggType === 'count' + aggType === Aggregators.COUNT ? {} - : aggType === 'rate' + : aggType === Aggregators.RATE ? networkTraffic('aggregatedValue', metric) : { aggregatedValue: { @@ -242,7 +242,8 @@ const getMetric: ( const comparatorMap = { [Comparator.BETWEEN]: (value: number, [a, b]: number[]) => value >= Math.min(a, b) && value <= Math.max(a, b), - // `threshold` is always an array of numbers in case the BETWEEN comparator is + [Comparator.OUTSIDE_RANGE]: (value: number, [a, b]: number[]) => value < a || value > b, + // `threshold` is always an array of numbers in case the BETWEEN/OUTSIDE_RANGE comparator is // used; all other compartors will just destructure the first value in the array [Comparator.GT]: (a: number, [b]: number[]) => a > b, [Comparator.LT]: (a: number, [b]: number[]) => a < b, diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts index 8808219cabaa7..b697af4fa4c3b 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts @@ -7,8 +7,15 @@ import { i18n } from '@kbn/i18n'; import uuid from 'uuid'; import { schema } from '@kbn/config-schema'; import { PluginSetupContract } from '../../../../../alerting/server'; +import { METRIC_EXPLORER_AGGREGATIONS } from '../../../../common/http_api/metrics_explorer'; import { createMetricThresholdExecutor, FIRED_ACTIONS } from './metric_threshold_executor'; -import { METRIC_THRESHOLD_ALERT_TYPE_ID } from './types'; +import { METRIC_THRESHOLD_ALERT_TYPE_ID, Comparator } from './types'; + +const oneOfLiterals = (arrayOfLiterals: Readonly) => + schema.string({ + validate: value => + arrayOfLiterals.includes(value) ? undefined : `must be one of ${arrayOfLiterals.join(' | ')}`, + }); export async function registerMetricThresholdAlertType(alertingPlugin: PluginSetupContract) { if (!alertingPlugin) { @@ -20,13 +27,7 @@ export async function registerMetricThresholdAlertType(alertingPlugin: PluginSet const baseCriterion = { threshold: schema.arrayOf(schema.number()), - comparator: schema.oneOf([ - schema.literal('>'), - schema.literal('<'), - schema.literal('>='), - schema.literal('<='), - schema.literal('between'), - ]), + comparator: oneOfLiterals(Object.values(Comparator)), timeUnit: schema.string(), timeSize: schema.number(), }; @@ -34,13 +35,7 @@ export async function registerMetricThresholdAlertType(alertingPlugin: PluginSet const nonCountCriterion = schema.object({ ...baseCriterion, metric: schema.string(), - aggType: schema.oneOf([ - schema.literal('avg'), - schema.literal('min'), - schema.literal('max'), - schema.literal('rate'), - schema.literal('cardinality'), - ]), + aggType: oneOfLiterals(METRIC_EXPLORER_AGGREGATIONS), }); const countCriterion = schema.object({ diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/types.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/types.ts index abed691f109c0..18f5503fe2c9e 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/types.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/types.ts @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { MetricsExplorerAggregation } from '../../../../common/http_api/metrics_explorer'; - export const METRIC_THRESHOLD_ALERT_TYPE_ID = 'metrics.alert.threshold'; export enum Comparator { @@ -14,6 +12,17 @@ export enum Comparator { GT_OR_EQ = '>=', LT_OR_EQ = '<=', BETWEEN = 'between', + OUTSIDE_RANGE = 'outside', +} + +export enum Aggregators { + COUNT = 'count', + AVERAGE = 'avg', + SUM = 'sum', + MIN = 'min', + MAX = 'max', + RATE = 'rate', + CARDINALITY = 'cardinality', } export enum AlertStates { @@ -34,7 +43,7 @@ interface BaseMetricExpressionParams { } interface NonCountMetricExpressionParams extends BaseMetricExpressionParams { - aggType: Exclude; + aggType: Exclude; metric: string; } From c0c3e76bc07598b81999c8f58d5ef58403802731 Mon Sep 17 00:00:00 2001 From: Tim Sullivan Date: Wed, 22 Apr 2020 11:09:22 -0700 Subject: [PATCH 08/39] add additional reporting config properties to docker whitelist (#63766) Co-authored-by: Elastic Machine --- .../os_packages/docker_generator/resources/bin/kibana-docker | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker index 38acfb15d3ece..73607ac24449c 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker @@ -197,10 +197,14 @@ kibana_vars=( xpack.reporting.csv.checkForFormulas xpack.reporting.csv.escapeFormulaValues xpack.reporting.csv.enablePanelActionDownload + xpack.reporting.csv.useByteOrderMarkEncoding xpack.reporting.csv.maxSizeBytes xpack.reporting.csv.scroll.duration xpack.reporting.csv.scroll.size xpack.reporting.capture.maxAttempts + xpack.reporting.capture.timeouts.openUrl + xpack.reporting.capture.timeouts.waitForElements + xpack.reporting.capture.timeouts.renderComplete xpack.reporting.enabled xpack.reporting.encryptionKey xpack.reporting.index From 5f269b7ee63834cfe32ddb6406f7cd0004897d3d Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Wed, 22 Apr 2020 21:15:51 +0200 Subject: [PATCH 09/39] [Discover] Unskip functional tests for field visualize buttons (#62614) --- .../apps/discover/feature_controls/discover_security.ts | 3 +-- .../apps/discover/feature_controls/discover_spaces.ts | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts index dc8c488460100..76ca613af4b55 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts @@ -28,8 +28,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.timePicker.setDefaultAbsoluteRange(); } - // FLAKY: https://github.com/elastic/kibana/issues/60535 - describe.skip('security', () => { + describe('security', () => { before(async () => { await esArchiver.load('discover/feature_controls/security'); await esArchiver.loadIfNeeded('logstash_functional'); diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_spaces.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_spaces.ts index f33b8b4899d16..4bedc757f0b57 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_spaces.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_spaces.ts @@ -24,8 +24,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.timePicker.setDefaultAbsoluteRange(); } - // FLAKY: https://github.com/elastic/kibana/issues/60559 - describe.skip('spaces', () => { + describe('spaces', () => { before(async () => { await esArchiver.loadIfNeeded('logstash_functional'); }); From 51479fda9b9682a82587bfec6c70d045f2eb4eba Mon Sep 17 00:00:00 2001 From: nnamdifrankie <56440728+nnamdifrankie@users.noreply.github.com> Date: Wed, 22 Apr 2020 15:17:56 -0400 Subject: [PATCH 10/39] [Endpoint,Ingest]EMT-357: Move Exported Ingest Manager Services to plugin Start (#64138) [Endpoint,Ingest]EMT-357: Move Exported Ingest Manager Services to plugin Start --- .../endpoint_app_context_services.test.ts | 14 +++++++ .../server/endpoint_app_context_services.ts | 40 +++++++++++++++++++ .../plugins/endpoint/server/index_pattern.ts | 2 +- x-pack/plugins/endpoint/server/mocks.ts | 7 ++-- x-pack/plugins/endpoint/server/plugin.test.ts | 29 +++++++++++--- x-pack/plugins/endpoint/server/plugin.ts | 32 ++++++++++----- .../server/routes/alerts/alerts.test.ts | 12 +++++- .../server/routes/alerts/details/handlers.ts | 4 +- .../server/routes/alerts/list/handlers.ts | 4 +- .../endpoint/server/routes/index_pattern.ts | 6 +-- .../endpoint/server/routes/metadata/index.ts | 22 +++++----- .../server/routes/metadata/metadata.test.ts | 11 ++++- .../routes/metadata/query_builders.test.ts | 13 ++---- .../endpoint/server/routes/resolver.ts | 7 ++-- .../server/routes/resolver/children.ts | 5 ++- .../server/routes/resolver/lifecycle.ts | 6 +-- .../server/routes/resolver/related_events.ts | 5 ++- x-pack/plugins/endpoint/server/types.ts | 10 +++-- .../ingest_manager/common/types/index.ts | 19 +++++++++ x-pack/plugins/ingest_manager/server/index.ts | 3 -- .../plugins/ingest_manager/server/plugin.ts | 37 +++++------------ .../server/services/es_index_pattern.ts | 11 +---- .../ingest_manager/server/services/index.ts | 2 +- 23 files changed, 200 insertions(+), 101 deletions(-) create mode 100644 x-pack/plugins/endpoint/server/endpoint_app_context_services.test.ts create mode 100644 x-pack/plugins/endpoint/server/endpoint_app_context_services.ts diff --git a/x-pack/plugins/endpoint/server/endpoint_app_context_services.test.ts b/x-pack/plugins/endpoint/server/endpoint_app_context_services.test.ts new file mode 100644 index 0000000000000..943a9c22c6aae --- /dev/null +++ b/x-pack/plugins/endpoint/server/endpoint_app_context_services.test.ts @@ -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. + */ +import { EndpointAppContextService } from './endpoint_app_context_services'; + +describe('test endpoint app context services', () => { + it('should throw error if start is not called', async () => { + const endpointAppContextService = new EndpointAppContextService(); + expect(() => endpointAppContextService.getIndexPatternRetriever()).toThrow(Error); + expect(() => endpointAppContextService.getAgentService()).toThrow(Error); + }); +}); diff --git a/x-pack/plugins/endpoint/server/endpoint_app_context_services.ts b/x-pack/plugins/endpoint/server/endpoint_app_context_services.ts new file mode 100644 index 0000000000000..4d77c5c68c69f --- /dev/null +++ b/x-pack/plugins/endpoint/server/endpoint_app_context_services.ts @@ -0,0 +1,40 @@ +/* + * 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 { IndexPatternRetriever } from './index_pattern'; +import { AgentService } from '../../ingest_manager/common/types'; + +/** + * A singleton that holds shared services that are initialized during the start up phase + * of the plugin lifecycle. And stop during the stop phase, if needed. + */ +export class EndpointAppContextService { + private indexPatternRetriever: IndexPatternRetriever | undefined; + private agentService: AgentService | undefined; + + public start(dependencies: { + indexPatternRetriever: IndexPatternRetriever; + agentService: AgentService; + }) { + this.indexPatternRetriever = dependencies.indexPatternRetriever; + this.agentService = dependencies.agentService; + } + + public stop() {} + + public getAgentService(): AgentService { + if (!this.agentService) { + throw new Error(`must call start on ${EndpointAppContextService.name} to call getter`); + } + return this.agentService; + } + + public getIndexPatternRetriever(): IndexPatternRetriever { + if (!this.indexPatternRetriever) { + throw new Error(`must call start on ${EndpointAppContextService.name} to call getter`); + } + return this.indexPatternRetriever; + } +} diff --git a/x-pack/plugins/endpoint/server/index_pattern.ts b/x-pack/plugins/endpoint/server/index_pattern.ts index ea612bfd75441..05cfd91f2777a 100644 --- a/x-pack/plugins/endpoint/server/index_pattern.ts +++ b/x-pack/plugins/endpoint/server/index_pattern.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import { Logger, LoggerFactory, RequestHandlerContext } from 'kibana/server'; -import { ESIndexPatternService } from '../../ingest_manager/server'; import { EndpointAppConstants } from '../common/types'; +import { ESIndexPatternService } from '../../ingest_manager/common/types'; export interface IndexPatternRetriever { getIndexPattern(ctx: RequestHandlerContext, datasetPath: string): Promise; diff --git a/x-pack/plugins/endpoint/server/mocks.ts b/x-pack/plugins/endpoint/server/mocks.ts index 3881840efe9df..8d677cf37f066 100644 --- a/x-pack/plugins/endpoint/server/mocks.ts +++ b/x-pack/plugins/endpoint/server/mocks.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IngestManagerSetupContract } from '../../ingest_manager/server'; -import { AgentService } from '../../ingest_manager/common/types'; +import { AgentService, IngestManagerStartupContract } from '../../ingest_manager/common/types'; /** * Creates a mock IndexPatternRetriever for use in tests. @@ -47,9 +46,9 @@ export const createMockAgentService = (): jest.Mocked => { * @param indexPattern a string index pattern to return when called by a test * @returns the same value as `indexPattern` parameter */ -export const createMockIngestManagerSetupContract = ( +export const createMockIngestManagerStartupContract = ( indexPattern: string -): IngestManagerSetupContract => { +): IngestManagerStartupContract => { return { esIndexPatternService: { getESIndexPattern: jest.fn().mockResolvedValue(indexPattern), diff --git a/x-pack/plugins/endpoint/server/plugin.test.ts b/x-pack/plugins/endpoint/server/plugin.test.ts index c380bc5c3e3d0..44b9c3eabbd6d 100644 --- a/x-pack/plugins/endpoint/server/plugin.test.ts +++ b/x-pack/plugins/endpoint/server/plugin.test.ts @@ -4,15 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EndpointPlugin, EndpointPluginSetupDependencies } from './plugin'; +import { + EndpointPlugin, + EndpointPluginSetupDependencies, + EndpointPluginStartDependencies, +} from './plugin'; import { coreMock } from '../../../../src/core/server/mocks'; import { PluginSetupContract } from '../../features/server'; -import { createMockIngestManagerSetupContract } from './mocks'; +import { createMockIngestManagerStartupContract } from './mocks'; describe('test endpoint plugin', () => { let plugin: EndpointPlugin; let mockCoreSetup: ReturnType; + let mockCoreStart: ReturnType; let mockedEndpointPluginSetupDependencies: jest.Mocked; + let mockedEndpointPluginStartDependencies: jest.Mocked; let mockedPluginSetupContract: jest.Mocked; beforeEach(() => { plugin = new EndpointPlugin( @@ -23,21 +29,32 @@ describe('test endpoint plugin', () => { ); mockCoreSetup = coreMock.createSetup(); + mockCoreStart = coreMock.createStart(); mockedPluginSetupContract = { registerFeature: jest.fn(), getFeatures: jest.fn(), getFeaturesUICapabilities: jest.fn(), registerLegacyAPI: jest.fn(), }; - mockedEndpointPluginSetupDependencies = { - features: mockedPluginSetupContract, - ingestManager: createMockIngestManagerSetupContract(''), - }; }); it('test properly setup plugin', async () => { + mockedEndpointPluginSetupDependencies = { + features: mockedPluginSetupContract, + }; await plugin.setup(mockCoreSetup, mockedEndpointPluginSetupDependencies); expect(mockedPluginSetupContract.registerFeature).toBeCalledTimes(1); expect(mockCoreSetup.http.createRouter).toBeCalledTimes(1); + expect(() => plugin.getEndpointAppContextService().getIndexPatternRetriever()).toThrow(Error); + expect(() => plugin.getEndpointAppContextService().getAgentService()).toThrow(Error); + }); + + it('test properly start plugin', async () => { + mockedEndpointPluginStartDependencies = { + ingestManager: createMockIngestManagerStartupContract(''), + }; + await plugin.start(mockCoreStart, mockedEndpointPluginStartDependencies); + expect(plugin.getEndpointAppContextService().getAgentService()).toBeTruthy(); + expect(plugin.getEndpointAppContextService().getIndexPatternRetriever()).toBeTruthy(); }); }); diff --git a/x-pack/plugins/endpoint/server/plugin.ts b/x-pack/plugins/endpoint/server/plugin.ts index ce6be5aeaf6db..f7ff32bdbfcbf 100644 --- a/x-pack/plugins/endpoint/server/plugin.ts +++ b/x-pack/plugins/endpoint/server/plugin.ts @@ -3,10 +3,9 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { Plugin, CoreSetup, PluginInitializerContext, Logger } from 'kibana/server'; +import { Plugin, CoreSetup, PluginInitializerContext, Logger, CoreStart } from 'kibana/server'; import { first } from 'rxjs/operators'; import { PluginSetupContract as FeaturesPluginSetupContract } from '../../features/server'; -import { IngestManagerSetupContract } from '../../ingest_manager/server'; import { createConfig$, EndpointConfigType } from './config'; import { EndpointAppContext } from './types'; @@ -15,14 +14,17 @@ import { registerResolverRoutes } from './routes/resolver'; import { registerIndexPatternRoute } from './routes/index_pattern'; import { registerEndpointRoutes } from './routes/metadata'; import { IngestIndexPatternRetriever } from './index_pattern'; +import { IngestManagerStartupContract } from '../../ingest_manager/common/types'; +import { EndpointAppContextService } from './endpoint_app_context_services'; export type EndpointPluginStart = void; export type EndpointPluginSetup = void; -export interface EndpointPluginStartDependencies {} // eslint-disable-line @typescript-eslint/no-empty-interface +export interface EndpointPluginStartDependencies { + ingestManager: IngestManagerStartupContract; +} export interface EndpointPluginSetupDependencies { features: FeaturesPluginSetupContract; - ingestManager: IngestManagerSetupContract; } export class EndpointPlugin @@ -34,9 +36,15 @@ export class EndpointPlugin EndpointPluginStartDependencies > { private readonly logger: Logger; + private readonly endpointAppContextService: EndpointAppContextService = new EndpointAppContextService(); constructor(private readonly initializerContext: PluginInitializerContext) { this.logger = this.initializerContext.logger.get('endpoint'); } + + public getEndpointAppContextService(): EndpointAppContextService { + return this.endpointAppContextService; + } + public setup(core: CoreSetup, plugins: EndpointPluginSetupDependencies) { plugins.features.registerFeature({ id: 'endpoint', @@ -66,12 +74,8 @@ export class EndpointPlugin }, }); const endpointContext = { - indexPatternRetriever: new IngestIndexPatternRetriever( - plugins.ingestManager.esIndexPatternService, - this.initializerContext.logger - ), - agentService: plugins.ingestManager.agentService, logFactory: this.initializerContext.logger, + service: this.endpointAppContextService, config: (): Promise => { return createConfig$(this.initializerContext) .pipe(first()) @@ -85,10 +89,18 @@ export class EndpointPlugin registerIndexPatternRoute(router, endpointContext); } - public start() { + public start(core: CoreStart, plugins: EndpointPluginStartDependencies) { this.logger.debug('Starting plugin'); + this.endpointAppContextService.start({ + indexPatternRetriever: new IngestIndexPatternRetriever( + plugins.ingestManager.esIndexPatternService, + this.initializerContext.logger + ), + agentService: plugins.ingestManager.agentService, + }); } public stop() { this.logger.debug('Stopping plugin'); + this.endpointAppContextService.stop(); } } diff --git a/x-pack/plugins/endpoint/server/routes/alerts/alerts.test.ts b/x-pack/plugins/endpoint/server/routes/alerts/alerts.test.ts index 39fc2ba4c74bb..1124c977ff924 100644 --- a/x-pack/plugins/endpoint/server/routes/alerts/alerts.test.ts +++ b/x-pack/plugins/endpoint/server/routes/alerts/alerts.test.ts @@ -13,25 +13,35 @@ import { registerAlertRoutes } from './index'; import { EndpointConfigSchema } from '../../config'; import { alertingIndexGetQuerySchema } from '../../../common/schema/alert_index'; import { createMockAgentService, createMockIndexPatternRetriever } from '../../mocks'; +import { EndpointAppContextService } from '../../endpoint_app_context_services'; describe('test alerts route', () => { let routerMock: jest.Mocked; let mockClusterClient: jest.Mocked; let mockScopedClient: jest.Mocked; + let endpointAppContextService: EndpointAppContextService; beforeEach(() => { mockClusterClient = elasticsearchServiceMock.createClusterClient(); mockScopedClient = elasticsearchServiceMock.createScopedClusterClient(); mockClusterClient.asScoped.mockReturnValue(mockScopedClient); routerMock = httpServiceMock.createRouter(); - registerAlertRoutes(routerMock, { + + endpointAppContextService = new EndpointAppContextService(); + endpointAppContextService.start({ indexPatternRetriever: createMockIndexPatternRetriever('events-endpoint-*'), agentService: createMockAgentService(), + }); + + registerAlertRoutes(routerMock, { logFactory: loggingServiceMock.create(), + service: endpointAppContextService, config: () => Promise.resolve(EndpointConfigSchema.validate({})), }); }); + afterEach(() => endpointAppContextService.stop()); + it('should fail to validate when `page_size` is not a number', async () => { const validate = () => { alertingIndexGetQuerySchema.validate({ diff --git a/x-pack/plugins/endpoint/server/routes/alerts/details/handlers.ts b/x-pack/plugins/endpoint/server/routes/alerts/details/handlers.ts index 9055ee4110fbb..e438ab853f3b5 100644 --- a/x-pack/plugins/endpoint/server/routes/alerts/details/handlers.ts +++ b/x-pack/plugins/endpoint/server/routes/alerts/details/handlers.ts @@ -26,7 +26,9 @@ export const alertDetailsHandlerWrapper = function( id: alertId, })) as GetResponse; - const indexPattern = await endpointAppContext.indexPatternRetriever.getEventIndexPattern(ctx); + const indexPattern = await endpointAppContext.service + .getIndexPatternRetriever() + .getEventIndexPattern(ctx); const config = await endpointAppContext.config(); const pagination: AlertDetailsPagination = new AlertDetailsPagination( diff --git a/x-pack/plugins/endpoint/server/routes/alerts/list/handlers.ts b/x-pack/plugins/endpoint/server/routes/alerts/list/handlers.ts index f23dffd13db4f..44a0cf8744a9e 100644 --- a/x-pack/plugins/endpoint/server/routes/alerts/list/handlers.ts +++ b/x-pack/plugins/endpoint/server/routes/alerts/list/handlers.ts @@ -18,7 +18,9 @@ export const alertListHandlerWrapper = function( res ) => { try { - const indexPattern = await endpointAppContext.indexPatternRetriever.getEventIndexPattern(ctx); + const indexPattern = await endpointAppContext.service + .getIndexPatternRetriever() + .getEventIndexPattern(ctx); const reqData = await getRequestData(req, endpointAppContext); const response = await searchESForAlerts( ctx.core.elasticsearch.dataClient, diff --git a/x-pack/plugins/endpoint/server/routes/index_pattern.ts b/x-pack/plugins/endpoint/server/routes/index_pattern.ts index 3b71f6a6957ba..79083f5f05e14 100644 --- a/x-pack/plugins/endpoint/server/routes/index_pattern.ts +++ b/x-pack/plugins/endpoint/server/routes/index_pattern.ts @@ -8,14 +8,14 @@ import { IRouter, Logger, RequestHandler } from 'kibana/server'; import { EndpointAppContext } from '../types'; import { IndexPatternGetParamsResult, EndpointAppConstants } from '../../common/types'; import { indexPatternGetParamsSchema } from '../../common/schema/index_pattern'; -import { IndexPatternRetriever } from '../index_pattern'; function handleIndexPattern( log: Logger, - indexRetriever: IndexPatternRetriever + endpointAppContext: EndpointAppContext ): RequestHandler { return async (context, req, res) => { try { + const indexRetriever = endpointAppContext.service.getIndexPatternRetriever(); return res.ok({ body: { indexPattern: await indexRetriever.getIndexPattern(context, req.params.datasetPath), @@ -37,6 +37,6 @@ export function registerIndexPatternRoute(router: IRouter, endpointAppContext: E validate: { params: indexPatternGetParamsSchema }, options: { authRequired: true }, }, - handleIndexPattern(log, endpointAppContext.indexPatternRetriever) + handleIndexPattern(log, endpointAppContext) ); } diff --git a/x-pack/plugins/endpoint/server/routes/metadata/index.ts b/x-pack/plugins/endpoint/server/routes/metadata/index.ts index bc79b828576e0..99dc4ac9f9e33 100644 --- a/x-pack/plugins/endpoint/server/routes/metadata/index.ts +++ b/x-pack/plugins/endpoint/server/routes/metadata/index.ts @@ -61,9 +61,9 @@ export function registerEndpointRoutes(router: IRouter, endpointAppContext: Endp }, async (context, req, res) => { try { - const index = await endpointAppContext.indexPatternRetriever.getMetadataIndexPattern( - context - ); + const index = await endpointAppContext.service + .getIndexPatternRetriever() + .getMetadataIndexPattern(context); const queryParams = await kibanaRequestToMetadataListESQuery( req, endpointAppContext, @@ -117,9 +117,9 @@ export async function getHostData( metadataRequestContext: MetadataRequestContext, id: string ): Promise { - const index = await metadataRequestContext.endpointAppContext.indexPatternRetriever.getMetadataIndexPattern( - metadataRequestContext.requestHandlerContext - ); + const index = await metadataRequestContext.endpointAppContext.service + .getIndexPatternRetriever() + .getMetadataIndexPattern(metadataRequestContext.requestHandlerContext); const query = getESQueryHostMetadataByID(id, index); const response = (await metadataRequestContext.requestHandlerContext.core.elasticsearch.dataClient.callAsCurrentUser( 'search', @@ -179,10 +179,12 @@ async function enrichHostMetadata( log.warn(`Missing elastic agent id, using host id instead ${elasticAgentId}`); } - const status = await metadataRequestContext.endpointAppContext.agentService.getAgentStatusById( - metadataRequestContext.requestHandlerContext.core.savedObjects.client, - elasticAgentId - ); + const status = await metadataRequestContext.endpointAppContext.service + .getAgentService() + .getAgentStatusById( + metadataRequestContext.requestHandlerContext.core.savedObjects.client, + elasticAgentId + ); hostStatus = HOST_STATUS_MAPPING.get(status) || HostStatus.ERROR; } catch (e) { if (e.isBoom && e.output.statusCode === 404) { diff --git a/x-pack/plugins/endpoint/server/routes/metadata/metadata.test.ts b/x-pack/plugins/endpoint/server/routes/metadata/metadata.test.ts index a1186aabc7a66..84930118df238 100644 --- a/x-pack/plugins/endpoint/server/routes/metadata/metadata.test.ts +++ b/x-pack/plugins/endpoint/server/routes/metadata/metadata.test.ts @@ -28,6 +28,7 @@ import * as data from '../../test_data/all_metadata_data.json'; import { createMockAgentService, createMockMetadataIndexPatternRetriever } from '../../mocks'; import { AgentService } from '../../../../ingest_manager/common/types'; import Boom from 'boom'; +import { EndpointAppContextService } from '../../endpoint_app_context_services'; describe('test endpoint route', () => { let routerMock: jest.Mocked; @@ -38,6 +39,7 @@ describe('test endpoint route', () => { let routeHandler: RequestHandler; let routeConfig: RouteConfig; let mockAgentService: jest.Mocked; + let endpointAppContextService: EndpointAppContextService; beforeEach(() => { mockClusterClient = elasticsearchServiceMock.createClusterClient() as jest.Mocked< @@ -49,14 +51,21 @@ describe('test endpoint route', () => { routerMock = httpServiceMock.createRouter(); mockResponse = httpServerMock.createResponseFactory(); mockAgentService = createMockAgentService(); - registerEndpointRoutes(routerMock, { + endpointAppContextService = new EndpointAppContextService(); + endpointAppContextService.start({ indexPatternRetriever: createMockMetadataIndexPatternRetriever(), agentService: mockAgentService, + }); + + registerEndpointRoutes(routerMock, { logFactory: loggingServiceMock.create(), + service: endpointAppContextService, config: () => Promise.resolve(EndpointConfigSchema.validate({})), }); }); + afterEach(() => endpointAppContextService.stop()); + function createRouteHandlerContext( dataClient: jest.Mocked, savedObjectsClient: jest.Mocked diff --git a/x-pack/plugins/endpoint/server/routes/metadata/query_builders.test.ts b/x-pack/plugins/endpoint/server/routes/metadata/query_builders.test.ts index 7e6e3f875cd4c..28bac2fa10e0c 100644 --- a/x-pack/plugins/endpoint/server/routes/metadata/query_builders.test.ts +++ b/x-pack/plugins/endpoint/server/routes/metadata/query_builders.test.ts @@ -6,11 +6,8 @@ import { httpServerMock, loggingServiceMock } from '../../../../../../src/core/server/mocks'; import { EndpointConfigSchema } from '../../config'; import { kibanaRequestToMetadataListESQuery, getESQueryHostMetadataByID } from './query_builders'; -import { - createMockAgentService, - createMockMetadataIndexPatternRetriever, - MetadataIndexPattern, -} from '../../mocks'; +import { MetadataIndexPattern } from '../../mocks'; +import { EndpointAppContextService } from '../../endpoint_app_context_services'; describe('query builder', () => { describe('MetadataListESQuery', () => { @@ -21,9 +18,8 @@ describe('query builder', () => { const query = await kibanaRequestToMetadataListESQuery( mockRequest, { - indexPatternRetriever: createMockMetadataIndexPatternRetriever(), - agentService: createMockAgentService(), logFactory: loggingServiceMock.create(), + service: new EndpointAppContextService(), config: () => Promise.resolve(EndpointConfigSchema.validate({})), }, MetadataIndexPattern @@ -73,9 +69,8 @@ describe('query builder', () => { const query = await kibanaRequestToMetadataListESQuery( mockRequest, { - indexPatternRetriever: createMockMetadataIndexPatternRetriever(), - agentService: createMockAgentService(), logFactory: loggingServiceMock.create(), + service: new EndpointAppContextService(), config: () => Promise.resolve(EndpointConfigSchema.validate({})), }, MetadataIndexPattern diff --git a/x-pack/plugins/endpoint/server/routes/resolver.ts b/x-pack/plugins/endpoint/server/routes/resolver.ts index 77fcbc87baeb1..a96d431225b15 100644 --- a/x-pack/plugins/endpoint/server/routes/resolver.ts +++ b/x-pack/plugins/endpoint/server/routes/resolver.ts @@ -12,7 +12,6 @@ import { handleLifecycle, validateLifecycle } from './resolver/lifecycle'; export function registerResolverRoutes(router: IRouter, endpointAppContext: EndpointAppContext) { const log = endpointAppContext.logFactory.get('resolver'); - const indexPatternService = endpointAppContext.indexPatternRetriever; router.get( { @@ -20,7 +19,7 @@ export function registerResolverRoutes(router: IRouter, endpointAppContext: Endp validate: validateRelatedEvents, options: { authRequired: true }, }, - handleRelatedEvents(log, indexPatternService) + handleRelatedEvents(log, endpointAppContext) ); router.get( @@ -29,7 +28,7 @@ export function registerResolverRoutes(router: IRouter, endpointAppContext: Endp validate: validateChildren, options: { authRequired: true }, }, - handleChildren(log, indexPatternService) + handleChildren(log, endpointAppContext) ); router.get( @@ -38,6 +37,6 @@ export function registerResolverRoutes(router: IRouter, endpointAppContext: Endp validate: validateLifecycle, options: { authRequired: true }, }, - handleLifecycle(log, indexPatternService) + handleLifecycle(log, endpointAppContext) ); } diff --git a/x-pack/plugins/endpoint/server/routes/resolver/children.ts b/x-pack/plugins/endpoint/server/routes/resolver/children.ts index 05b8f0b5f8608..c3b19b6c912b6 100644 --- a/x-pack/plugins/endpoint/server/routes/resolver/children.ts +++ b/x-pack/plugins/endpoint/server/routes/resolver/children.ts @@ -11,7 +11,7 @@ import { extractEntityID } from './utils/normalize'; import { getPaginationParams } from './utils/pagination'; import { LifecycleQuery } from './queries/lifecycle'; import { ChildrenQuery } from './queries/children'; -import { IndexPatternRetriever } from '../../index_pattern'; +import { EndpointAppContext } from '../../types'; interface ChildrenQueryParams { after?: string; @@ -47,7 +47,7 @@ export const validateChildren = { export function handleChildren( log: Logger, - indexRetriever: IndexPatternRetriever + endpointAppContext: EndpointAppContext ): RequestHandler { return async (context, req, res) => { const { @@ -55,6 +55,7 @@ export function handleChildren( query: { limit, after, legacyEndpointID }, } = req; try { + const indexRetriever = endpointAppContext.service.getIndexPatternRetriever(); const pagination = getPaginationParams(limit, after); const indexPattern = await indexRetriever.getEventIndexPattern(context); diff --git a/x-pack/plugins/endpoint/server/routes/resolver/lifecycle.ts b/x-pack/plugins/endpoint/server/routes/resolver/lifecycle.ts index 6d155b79651a7..91a4c5d49bc54 100644 --- a/x-pack/plugins/endpoint/server/routes/resolver/lifecycle.ts +++ b/x-pack/plugins/endpoint/server/routes/resolver/lifecycle.ts @@ -4,13 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import _ from 'lodash'; import { schema } from '@kbn/config-schema'; import { RequestHandler, Logger } from 'kibana/server'; import { extractParentEntityID } from './utils/normalize'; import { LifecycleQuery } from './queries/lifecycle'; import { ResolverEvent } from '../../../common/types'; -import { IndexPatternRetriever } from '../../index_pattern'; +import { EndpointAppContext } from '../../types'; interface LifecycleQueryParams { ancestors: number; @@ -48,7 +47,7 @@ function getParentEntityID(results: ResolverEvent[]) { export function handleLifecycle( log: Logger, - indexRetriever: IndexPatternRetriever + endpointAppContext: EndpointAppContext ): RequestHandler { return async (context, req, res) => { const { @@ -56,6 +55,7 @@ export function handleLifecycle( query: { ancestors, legacyEndpointID }, } = req; try { + const indexRetriever = endpointAppContext.service.getIndexPatternRetriever(); const ancestorLifecycles = []; const client = context.core.elasticsearch.dataClient; const indexPattern = await indexRetriever.getEventIndexPattern(context); diff --git a/x-pack/plugins/endpoint/server/routes/resolver/related_events.ts b/x-pack/plugins/endpoint/server/routes/resolver/related_events.ts index 46e205464f53c..83e111a1e62e6 100644 --- a/x-pack/plugins/endpoint/server/routes/resolver/related_events.ts +++ b/x-pack/plugins/endpoint/server/routes/resolver/related_events.ts @@ -8,7 +8,7 @@ import { schema } from '@kbn/config-schema'; import { RequestHandler, Logger } from 'kibana/server'; import { getPaginationParams } from './utils/pagination'; import { RelatedEventsQuery } from './queries/related_events'; -import { IndexPatternRetriever } from '../../index_pattern'; +import { EndpointAppContext } from '../../types'; interface RelatedEventsQueryParams { after?: string; @@ -44,7 +44,7 @@ export const validateRelatedEvents = { export function handleRelatedEvents( log: Logger, - indexRetriever: IndexPatternRetriever + endpointAppContext: EndpointAppContext ): RequestHandler { return async (context, req, res) => { const { @@ -52,6 +52,7 @@ export function handleRelatedEvents( query: { limit, after, legacyEndpointID }, } = req; try { + const indexRetriever = endpointAppContext.service.getIndexPatternRetriever(); const pagination = getPaginationParams(limit, after); const client = context.core.elasticsearch.dataClient; diff --git a/x-pack/plugins/endpoint/server/types.ts b/x-pack/plugins/endpoint/server/types.ts index d43ec58aec428..dfa5950adba5c 100644 --- a/x-pack/plugins/endpoint/server/types.ts +++ b/x-pack/plugins/endpoint/server/types.ts @@ -5,15 +5,17 @@ */ import { LoggerFactory } from 'kibana/server'; import { EndpointConfigType } from './config'; -import { IndexPatternRetriever } from './index_pattern'; -import { AgentService } from '../../ingest_manager/common/types'; +import { EndpointAppContextService } from './endpoint_app_context_services'; /** * The context for Endpoint apps. */ export interface EndpointAppContext { - indexPatternRetriever: IndexPatternRetriever; - agentService: AgentService; logFactory: LoggerFactory; config(): Promise; + + /** + * Object readiness is tied to plugin start method + */ + service: EndpointAppContextService; } diff --git a/x-pack/plugins/ingest_manager/common/types/index.ts b/x-pack/plugins/ingest_manager/common/types/index.ts index 150a4c9d60280..438db9a25b8ee 100644 --- a/x-pack/plugins/ingest_manager/common/types/index.ts +++ b/x-pack/plugins/ingest_manager/common/types/index.ts @@ -9,6 +9,25 @@ import { AgentStatus } from './models'; export * from './models'; export * from './rest_spec'; +/** + * Service to return the index pattern of EPM packages + */ +export interface ESIndexPatternService { + getESIndexPattern( + savedObjectsClient: SavedObjectsClientContract, + pkgName: string, + datasetPath: string + ): Promise; +} + +/** + * Describes public IngestManager plugin contract returned at the `startup` stage. + */ +export interface IngestManagerStartupContract { + esIndexPatternService: ESIndexPatternService; + agentService: AgentService; +} + /** * A service that provides exported functions that return information about an Agent */ diff --git a/x-pack/plugins/ingest_manager/server/index.ts b/x-pack/plugins/ingest_manager/server/index.ts index d99eb2a9bb4bb..0f49de0d5fdd1 100644 --- a/x-pack/plugins/ingest_manager/server/index.ts +++ b/x-pack/plugins/ingest_manager/server/index.ts @@ -7,9 +7,6 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { PluginInitializerContext } from 'src/core/server'; import { IngestManagerPlugin } from './plugin'; -export { ESIndexPatternService } from './services'; -export { IngestManagerSetupContract } from './plugin'; - export const config = { exposeToBrowser: { epm: true, diff --git a/x-pack/plugins/ingest_manager/server/plugin.ts b/x-pack/plugins/ingest_manager/server/plugin.ts index 075a0917b9fae..80e35eadb4603 100644 --- a/x-pack/plugins/ingest_manager/server/plugin.ts +++ b/x-pack/plugins/ingest_manager/server/plugin.ts @@ -39,22 +39,10 @@ import { registerInstallScriptRoutes, } from './routes'; -import { AgentService, IngestManagerConfigType } from '../common'; -import { - appContextService, - ESIndexPatternService, - ESIndexPatternSavedObjectService, -} from './services'; +import { IngestManagerConfigType, IngestManagerStartupContract } from '../common'; +import { appContextService, ESIndexPatternSavedObjectService } from './services'; import { getAgentStatusById } from './services/agents'; -/** - * Describes public IngestManager plugin contract returned at the `setup` stage. - */ -export interface IngestManagerSetupContract { - esIndexPatternService: ESIndexPatternService; - agentService: AgentService; -} - export interface IngestManagerSetupDeps { licensing: LicensingPluginSetup; security?: SecurityPluginSetup; @@ -78,7 +66,7 @@ const allSavedObjectTypes = [ ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, ]; -export class IngestManagerPlugin implements Plugin { +export class IngestManagerPlugin implements Plugin { private config$: Observable; private security: SecurityPluginSetup | undefined; @@ -86,10 +74,7 @@ export class IngestManagerPlugin implements Plugin { this.config$ = this.initializerContext.config.create(); } - public async setup( - core: CoreSetup, - deps: IngestManagerSetupDeps - ): Promise> { + public async setup(core: CoreSetup, deps: IngestManagerSetupDeps) { if (deps.security) { this.security = deps.security; } @@ -148,12 +133,6 @@ export class IngestManagerPlugin implements Plugin { basePath: core.http.basePath, }); } - return deepFreeze({ - esIndexPatternService: new ESIndexPatternSavedObjectService(), - agentService: { - getAgentStatusById, - }, - }); } public async start( @@ -161,13 +140,19 @@ export class IngestManagerPlugin implements Plugin { plugins: { encryptedSavedObjects: EncryptedSavedObjectsPluginStart; } - ) { + ): Promise> { appContextService.start({ encryptedSavedObjects: plugins.encryptedSavedObjects, security: this.security, config$: this.config$, savedObjects: core.savedObjects, }); + return deepFreeze({ + esIndexPatternService: new ESIndexPatternSavedObjectService(), + agentService: { + getAgentStatusById, + }, + }); } public async stop() { diff --git a/x-pack/plugins/ingest_manager/server/services/es_index_pattern.ts b/x-pack/plugins/ingest_manager/server/services/es_index_pattern.ts index 167e22873979c..e2c27cdacda2f 100644 --- a/x-pack/plugins/ingest_manager/server/services/es_index_pattern.ts +++ b/x-pack/plugins/ingest_manager/server/services/es_index_pattern.ts @@ -4,15 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import { SavedObjectsClientContract } from 'kibana/server'; -import { getInstallation } from './epm/packages/get'; - -export interface ESIndexPatternService { - getESIndexPattern( - savedObjectsClient: SavedObjectsClientContract, - pkgName: string, - datasetPath: string - ): Promise; -} +import { getInstallation } from './epm/packages'; +import { ESIndexPatternService } from '../../common/types'; export class ESIndexPatternSavedObjectService implements ESIndexPatternService { public async getESIndexPattern( diff --git a/x-pack/plugins/ingest_manager/server/services/index.ts b/x-pack/plugins/ingest_manager/server/services/index.ts index d64f1b0c2b6fb..4dfc3cb58b733 100644 --- a/x-pack/plugins/ingest_manager/server/services/index.ts +++ b/x-pack/plugins/ingest_manager/server/services/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ export { appContextService } from './app_context'; -export { ESIndexPatternService, ESIndexPatternSavedObjectService } from './es_index_pattern'; +export { ESIndexPatternSavedObjectService } from './es_index_pattern'; // Saved object services export { datasourceService } from './datasource'; From 23fbf7d67d0042366edd2ef958aa30d10207e924 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Wed, 22 Apr 2020 15:31:25 -0400 Subject: [PATCH 11/39] [Monitoring] Update docs based on config change (#63860) * Update a bunch of docs * PR feedback Co-authored-by: Elastic Machine --- docs/setup/docker.asciidoc | 6 ++-- docs/user/monitoring/cluster-alerts.asciidoc | 4 +-- .../monitoring/elasticsearch-details.asciidoc | 2 +- .../monitoring/monitoring-kibana.asciidoc | 4 +-- .../monitoring/monitoring-metricbeat.asciidoc | 4 +-- .../monitoring-troubleshooting.asciidoc | 2 +- docs/user/monitoring/viewing-metrics.asciidoc | 8 ++--- .../securing-communications/index.asciidoc | 4 +-- .../resources/bin/kibana-docker | 35 +++++++++---------- .../templates/kibana_yml.template.js | 4 +-- x-pack/legacy/plugins/monitoring/README.md | 4 +-- 11 files changed, 37 insertions(+), 40 deletions(-) diff --git a/docs/setup/docker.asciidoc b/docs/setup/docker.asciidoc index ddabce3d5b842..12ee96b21b0c7 100644 --- a/docs/setup/docker.asciidoc +++ b/docs/setup/docker.asciidoc @@ -94,7 +94,7 @@ Some example translations are shown here: **Environment Variable**:: **Kibana Setting** `SERVER_NAME`:: `server.name` `KIBANA_DEFAULTAPPID`:: `kibana.defaultAppId` -`XPACK_MONITORING_ENABLED`:: `xpack.monitoring.enabled` +`MONITORING_ENABLED`:: `monitoring.enabled` In general, any setting listed in <> can be configured with this technique. @@ -125,9 +125,9 @@ images: `server.name`:: `kibana` `server.host`:: `"0"` `elasticsearch.hosts`:: `http://elasticsearch:9200` -`xpack.monitoring.ui.container.elasticsearch.enabled`:: `true` +`monitoring.ui.container.elasticsearch.enabled`:: `true` -NOTE: The setting `xpack.monitoring.ui.container.elasticsearch.enabled` is not +NOTE: The setting `monitoring.ui.container.elasticsearch.enabled` is not defined in the `-oss` image. These settings are defined in the default `kibana.yml`. They can be overridden diff --git a/docs/user/monitoring/cluster-alerts.asciidoc b/docs/user/monitoring/cluster-alerts.asciidoc index cfdc9e2037030..a58ccc7f7d68d 100644 --- a/docs/user/monitoring/cluster-alerts.asciidoc +++ b/docs/user/monitoring/cluster-alerts.asciidoc @@ -49,13 +49,13 @@ To receive email notifications for the Cluster Alerts: . Configure an email account as described in {ref}/actions-email.html#configuring-email[Configuring email accounts]. . Configure the -`xpack.monitoring.cluster_alerts.email_notifications.email_address` setting in +`monitoring.cluster_alerts.email_notifications.email_address` setting in `kibana.yml` with your email address. + -- TIP: If you have separate production and monitoring clusters and separate {kib} instances for those clusters, you must put the -`xpack.monitoring.cluster_alerts.email_notifications.email_address` setting in +`monitoring.cluster_alerts.email_notifications.email_address` setting in the {kib} instance that is associated with the production cluster. -- diff --git a/docs/user/monitoring/elasticsearch-details.asciidoc b/docs/user/monitoring/elasticsearch-details.asciidoc index c0e804672d298..93f809cfff650 100644 --- a/docs/user/monitoring/elasticsearch-details.asciidoc +++ b/docs/user/monitoring/elasticsearch-details.asciidoc @@ -164,4 +164,4 @@ image::user/monitoring/images/monitoring-elasticsearch-logs.jpg["Recent {es} log TIP: By default, up to 10 log entries are shown. You can show up to 50 log entries by changing the -<>. +<>. diff --git a/docs/user/monitoring/monitoring-kibana.asciidoc b/docs/user/monitoring/monitoring-kibana.asciidoc index d0f2bd6acd901..9aa10289d299b 100644 --- a/docs/user/monitoring/monitoring-kibana.asciidoc +++ b/docs/user/monitoring/monitoring-kibana.asciidoc @@ -61,8 +61,8 @@ For more information, see {ref}/monitoring-settings.html[Monitoring settings in and {ref}/cluster-update-settings.html[Cluster update settings]. -- -. Verify that `xpack.monitoring.enabled` and -`xpack.monitoring.kibana.collection.enabled` are set to `true` in the +. Verify that `monitoring.enabled` and +`monitoring.kibana.collection.enabled` are set to `true` in the `kibana.yml` file. These are the default values. For more information, see <>. diff --git a/docs/user/monitoring/monitoring-metricbeat.asciidoc b/docs/user/monitoring/monitoring-metricbeat.asciidoc index f03a2ce1525a4..61aeaf21d3a4b 100644 --- a/docs/user/monitoring/monitoring-metricbeat.asciidoc +++ b/docs/user/monitoring/monitoring-metricbeat.asciidoc @@ -25,10 +25,10 @@ Add the following setting in the {kib} configuration file (`kibana.yml`): [source,yaml] ---------------------------------- -xpack.monitoring.kibana.collection.enabled: false +monitoring.kibana.collection.enabled: false ---------------------------------- -Leave the `xpack.monitoring.enabled` set to its default value (`true`). +Leave the `monitoring.enabled` set to its default value (`true`). // end::disable-kibana-collection[] For more information, see <>. diff --git a/docs/user/monitoring/monitoring-troubleshooting.asciidoc b/docs/user/monitoring/monitoring-troubleshooting.asciidoc index 7e1d6f94f15fa..bdaa10990c3aa 100644 --- a/docs/user/monitoring/monitoring-troubleshooting.asciidoc +++ b/docs/user/monitoring/monitoring-troubleshooting.asciidoc @@ -52,7 +52,7 @@ The *Stack Monitoring* page in {kib} is empty. . Confirm that {kib} is seeking monitoring data from the appropriate {es} URL. By default, data is retrieved from the cluster specified in the `elasticsearch.hosts` setting in the `kibana.yml` file. If you want to retrieve it -from a different monitoring cluster, set `xpack.monitoring.elasticsearch.hosts`. +from a different monitoring cluster, set `monitoring.ui.elasticsearch.hosts`. See <>. . Confirm that there is monitoring data available at that URL. It is stored in diff --git a/docs/user/monitoring/viewing-metrics.asciidoc b/docs/user/monitoring/viewing-metrics.asciidoc index 11516e32400fb..0a5535e6e1a91 100644 --- a/docs/user/monitoring/viewing-metrics.asciidoc +++ b/docs/user/monitoring/viewing-metrics.asciidoc @@ -26,14 +26,14 @@ cluster and view them all through the same instance of {kib}. By default, data is retrieved from the cluster specified in the `elasticsearch.hosts` value in the `kibana.yml` file. If you want to retrieve it -from a different cluster, set `xpack.monitoring.elasticsearch.hosts`. +from a different cluster, set `monitoring.ui.elasticsearch.hosts`. To learn more about typical monitoring architectures, see {ref}/how-monitoring-works.html[How monitoring works] and {ref}/monitoring-production.html[Monitoring in a production environment]. -- -. Verify that `xpack.monitoring.ui.enabled` is set to `true`, which is the +. Verify that `monitoring.ui.enabled` is set to `true`, which is the default value, in the `kibana.yml` file. For more information, see <>. @@ -43,8 +43,8 @@ must provide a user ID and password so {kib} can retrieve the data. .. Create a user that has the `monitoring_user` {ref}/built-in-roles.html[built-in role] on the monitoring cluster. -.. Add the `xpack.monitoring.elasticsearch.username` and -`xpack.monitoring.elasticsearch.password` settings in the `kibana.yml` file. +.. Add the `monitoring.ui.elasticsearch.username` and +`monitoring.ui.elasticsearch.password` settings in the `kibana.yml` file. If these settings are omitted, {kib} uses the `elasticsearch.username` and `elasticsearch.password` setting values. For more information, see {kibana-ref}/using-kibana-with-security.html[Configuring security in {kib}]. diff --git a/docs/user/security/securing-communications/index.asciidoc b/docs/user/security/securing-communications/index.asciidoc index 97313c19f44cb..3bdc59b90b3fd 100644 --- a/docs/user/security/securing-communications/index.asciidoc +++ b/docs/user/security/securing-communications/index.asciidoc @@ -188,5 +188,5 @@ verification. For more information about this setting, see < Date: Wed, 22 Apr 2020 16:25:55 -0400 Subject: [PATCH 12/39] [Ingest] Allow to reassign agent to a new config (#63847) --- .../ingest_manager/common/constants/routes.ts | 3 +- .../ingest_manager/common/services/routes.ts | 5 +- .../common/types/models/agent.ts | 2 +- .../common/types/rest_spec/agent.ts | 21 +- .../hooks/use_request/agents.ts | 15 ++ .../components/details_section.tsx | 77 ++++-- .../sections/fleet/agent_list_page/index.tsx | 237 +++++++----------- .../agent_reassign_config_flyout/index.tsx | 186 ++++++++++++++ .../components/agent_unenroll_provider.tsx | 55 ++-- .../sections/fleet/components/index.tsx | 1 + .../ingest_manager/types/index.ts | 2 + .../server/routes/agent/handlers.ts | 76 ++---- .../server/routes/agent/index.ts | 11 + .../server/services/agents/checkin.test.ts | 14 ++ .../server/services/agents/checkin.ts | 10 +- .../server/services/agents/index.ts | 1 + .../server/services/agents/reassign.ts | 28 +++ .../server/services/agents/unenroll.ts | 26 +- .../server/services/agents/update.ts | 9 +- .../server/types/rest_spec/agent.ts | 20 +- .../translations/translations/ja-JP.json | 9 - .../translations/translations/zh-CN.json | 9 - .../apis/fleet/unenroll_agent.ts | 49 +--- 23 files changed, 501 insertions(+), 365 deletions(-) create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_reassign_config_flyout/index.tsx create mode 100644 x-pack/plugins/ingest_manager/server/services/agents/reassign.ts diff --git a/x-pack/plugins/ingest_manager/common/constants/routes.ts b/x-pack/plugins/ingest_manager/common/constants/routes.ts index a31d38a723c2c..616195b32f266 100644 --- a/x-pack/plugins/ingest_manager/common/constants/routes.ts +++ b/x-pack/plugins/ingest_manager/common/constants/routes.ts @@ -53,7 +53,8 @@ export const AGENT_API_ROUTES = { ACKS_PATTERN: `${FLEET_API_ROOT}/agents/{agentId}/acks`, ACTIONS_PATTERN: `${FLEET_API_ROOT}/agents/{agentId}/actions`, ENROLL_PATTERN: `${FLEET_API_ROOT}/agents/enroll`, - UNENROLL_PATTERN: `${FLEET_API_ROOT}/agents/unenroll`, + UNENROLL_PATTERN: `${FLEET_API_ROOT}/agents/{agentId}/unenroll`, + REASSIGN_PATTERN: `${FLEET_API_ROOT}/agents/{agentId}/reassign`, STATUS_PATTERN: `${FLEET_API_ROOT}/agent-status`, }; diff --git a/x-pack/plugins/ingest_manager/common/services/routes.ts b/x-pack/plugins/ingest_manager/common/services/routes.ts index 7cc6fc3c66afb..f2343b1039151 100644 --- a/x-pack/plugins/ingest_manager/common/services/routes.ts +++ b/x-pack/plugins/ingest_manager/common/services/routes.ts @@ -97,7 +97,10 @@ export const agentRouteService = { getInfoPath: (agentId: string) => AGENT_API_ROUTES.INFO_PATTERN.replace('{agentId}', agentId), getUpdatePath: (agentId: string) => AGENT_API_ROUTES.UPDATE_PATTERN.replace('{agentId}', agentId), getEventsPath: (agentId: string) => AGENT_API_ROUTES.EVENTS_PATTERN.replace('{agentId}', agentId), - getUnenrollPath: () => AGENT_API_ROUTES.UNENROLL_PATTERN, + getUnenrollPath: (agentId: string) => + AGENT_API_ROUTES.UNENROLL_PATTERN.replace('{agentId}', agentId), + getReassignPath: (agentId: string) => + AGENT_API_ROUTES.REASSIGN_PATTERN.replace('{agentId}', agentId), getListPath: () => AGENT_API_ROUTES.LIST_PATTERN, getStatusPath: () => AGENT_API_ROUTES.STATUS_PATTERN, }; diff --git a/x-pack/plugins/ingest_manager/common/types/models/agent.ts b/x-pack/plugins/ingest_manager/common/types/models/agent.ts index 4d03a30f9a590..14b2b2e47d17f 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/agent.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/agent.ts @@ -65,7 +65,7 @@ interface AgentBase { access_api_key_id?: string; default_api_key?: string; config_id?: string; - config_revision?: number; + config_revision?: number | null; config_newest_revision?: number; last_checkin?: string; } diff --git a/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts index 21ab41740ce3e..64ed95db74f4c 100644 --- a/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts +++ b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts @@ -96,16 +96,23 @@ export interface PostNewAgentActionResponse { } export interface PostAgentUnenrollRequest { - body: { kuery: string } | { ids: string[] }; + params: { + agentId: string; + }; } export interface PostAgentUnenrollResponse { - results: Array<{ - success: boolean; - error?: any; - id: string; - action: string; - }>; + success: boolean; +} + +export interface PutAgentReassignRequest { + params: { + agentId: string; + }; + body: { config_id: string }; +} + +export interface PutAgentReassignResponse { success: boolean; } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agents.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agents.ts index f08b950e71ea8..453bcf2bd81e7 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agents.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agents.ts @@ -10,6 +10,8 @@ import { GetOneAgentResponse, GetOneAgentEventsResponse, GetOneAgentEventsRequest, + PutAgentReassignRequest, + PutAgentReassignResponse, GetAgentsRequest, GetAgentsResponse, GetAgentStatusRequest, @@ -59,3 +61,16 @@ export function sendGetAgentStatus( ...options, }); } + +export function sendPutAgentReassign( + agentId: string, + body: PutAgentReassignRequest['body'], + options?: RequestOptions +) { + return sendRequest({ + method: 'put', + path: agentRouteService.getReassignPath(agentId), + body, + ...options, + }); +} diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/details_section.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/details_section.tsx index 0844368dc214b..653e2eb9a3a3b 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/details_section.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/details_section.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState, Fragment } from 'react'; +import React, { useState, Fragment, useCallback } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiTitle, @@ -13,10 +13,13 @@ import { EuiFlexItem, EuiDescriptionList, EuiButton, + EuiPopover, EuiDescriptionListTitle, EuiDescriptionListDescription, EuiButtonEmpty, EuiIconTip, + EuiContextMenuPanel, + EuiContextMenuItem, EuiTextColor, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -26,7 +29,7 @@ import { Agent } from '../../../../types'; import { AgentHealth } from '../../components/agent_health'; import { useCapabilities, useGetOneAgentConfig } from '../../../../hooks'; import { Loading } from '../../../../components'; -import { ConnectedLink } from '../../components'; +import { ConnectedLink, AgentReassignConfigFlyout } from '../../components'; import { AgentUnenrollProvider } from '../../components/agent_unenroll_provider'; const Item: React.FunctionComponent<{ label: string }> = ({ label, children }) => { @@ -56,6 +59,15 @@ export const AgentDetailSection: React.FunctionComponent = ({ agent }) => const hasWriteCapabilites = useCapabilities().write; const metadataFlyout = useFlyout(); const refreshAgent = useAgentRefresh(); + // Actions menu + const [isActionsPopoverOpen, setIsActionsPopoverOpen] = useState(false); + const handleCloseMenu = useCallback(() => setIsActionsPopoverOpen(false), [ + setIsActionsPopoverOpen, + ]); + const handleToggleMenu = useCallback(() => setIsActionsPopoverOpen(!isActionsPopoverOpen), [ + isActionsPopoverOpen, + ]); + const [isReassignFlyoutOpen, setIsReassignFlyoutOpen] = useState(false); // Fetch AgentConfig information const { isLoading: isAgentConfigLoading, data: agentConfigData } = useGetOneAgentConfig( @@ -111,6 +123,9 @@ export const AgentDetailSection: React.FunctionComponent = ({ agent }) => return ( <> + {isReassignFlyoutOpen && ( + setIsReassignFlyoutOpen(false)} /> + )} @@ -123,21 +138,55 @@ export const AgentDetailSection: React.FunctionComponent = ({ agent }) => - - {unenrollAgentsPrompt => ( - { - unenrollAgentsPrompt([agent.id], 1, refreshAgent); - }} - > + - )} - + } + isOpen={isActionsPopoverOpen} + closePopover={handleCloseMenu} + > + { + handleCloseMenu(); + setIsReassignFlyoutOpen(true); + }} + key="reassignConfig" + > + + , + + + {unenrollAgentsPrompt => ( + { + unenrollAgentsPrompt([agent.id], 1, refreshAgent); + }} + > + + + )} + , + ]} + /> + diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx index d363c472f2305..c79255104a030 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx @@ -35,7 +35,7 @@ import { useUrlParams, useLink, } from '../../../hooks'; -import { ConnectedLink } from '../components'; +import { ConnectedLink, AgentReassignConfigFlyout } from '../components'; import { SearchBar } from '../../../components/search_bar'; import { AgentHealth } from '../components/agent_health'; import { AgentUnenrollProvider } from '../components/agent_unenroll_provider'; @@ -71,61 +71,76 @@ const statusFilters = [ }, ] as Array<{ label: string; status: string }>; -const RowActions = React.memo<{ agent: Agent; refresh: () => void }>(({ agent, refresh }) => { - const hasWriteCapabilites = useCapabilities().write; - const DETAILS_URI = useLink(FLEET_AGENT_DETAIL_PATH); - const [isOpen, setIsOpen] = useState(false); - const handleCloseMenu = useCallback(() => setIsOpen(false), [setIsOpen]); - const handleToggleMenu = useCallback(() => setIsOpen(!isOpen), [isOpen]); +const RowActions = React.memo<{ agent: Agent; onReassignClick: () => void; refresh: () => void }>( + ({ agent, refresh, onReassignClick }) => { + const hasWriteCapabilites = useCapabilities().write; + const DETAILS_URI = useLink(FLEET_AGENT_DETAIL_PATH); + const [isOpen, setIsOpen] = useState(false); + const handleCloseMenu = useCallback(() => setIsOpen(false), [setIsOpen]); + const handleToggleMenu = useCallback(() => setIsOpen(!isOpen), [isOpen]); - return ( - - } - isOpen={isOpen} - closePopover={handleCloseMenu} - > - - - , + return ( + + } + isOpen={isOpen} + closePopover={handleCloseMenu} + > + + + , + { + handleCloseMenu(); + onReassignClick(); + }} + key="reassignConfig" + > + + , - - {unenrollAgentsPrompt => ( - { - unenrollAgentsPrompt([agent.id], 1, () => { - refresh(); - }); - }} - > - - - )} - , - ]} - /> - - ); -}); + + {unenrollAgentsPrompt => ( + { + unenrollAgentsPrompt([agent.id], 1, () => { + refresh(); + }); + }} + > + + + )} + , + ]} + /> + + ); + } +); export const AgentListPage: React.FunctionComponent<{}> = () => { const defaultKuery: string = (useUrlParams().urlParams.kuery as string) || ''; @@ -136,8 +151,6 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { // Table and search states const [search, setSearch] = useState(defaultKuery); const { pagination, pageSizeOptions, setPagination } = usePagination(); - const [selectedAgents, setSelectedAgents] = useState([]); - const [areAllAgentsSelected, setAreAllAgentsSelected] = useState(false); // Configs state (for filtering) const [isConfigsFilterOpen, setIsConfigsFilterOpen] = useState(false); @@ -159,6 +172,9 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { // Agent enrollment flyout state const [isEnrollmentFlyoutOpen, setIsEnrollmentFlyoutOpen] = useState(false); + // Agent reassignment flyout state + const [agentToReassignId, setAgentToReassignId] = useState(undefined); + let kuery = search.trim(); if (selectedConfigs.length) { if (kuery) { @@ -227,47 +243,6 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { {host} ), - footer: () => { - if (selectedAgents.length === agents.length && totalAgents > selectedAgents.length) { - return areAllAgentsSelected ? ( - setAreAllAgentsSelected(false)}> - - - ), - }} - /> - ) : ( - setAreAllAgentsSelected(true)}> - - - ), - }} - /> - ); - } - return null; - }, }, { field: 'active', @@ -350,7 +325,13 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { actions: [ { render: (agent: Agent) => { - return agentsRequest.sendRequest()} />; + return ( + agentsRequest.sendRequest()} + onReassignClick={() => setAgentToReassignId(agent.id)} + /> + ); }, }, ], @@ -381,6 +362,8 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { /> ); + const agentToReassign = agentToReassignId && agents.find(a => a.id === agentToReassignId); + return ( <> {isEnrollmentFlyoutOpen ? ( @@ -389,48 +372,16 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { onClose={() => setIsEnrollmentFlyoutOpen(false)} /> ) : null} + {agentToReassign && ( + { + setAgentToReassignId(undefined); + agentsRequest.sendRequest(); + }} + /> + )} - {selectedAgents.length ? ( - - - {unenrollAgentsPrompt => ( - { - unenrollAgentsPrompt( - areAllAgentsSelected ? search : selectedAgents.map(agent => agent.id), - areAllAgentsSelected ? totalAgents : selectedAgents.length, - () => { - // Reload agents if on first page and no search query, otherwise - // reset to first page and reset search, which will trigger a reload - if (pagination.currentPage === 1 && !search) { - agentsRequest.sendRequest(); - } else { - setPagination({ - ...pagination, - currentPage: 1, - }); - setSearch(''); - } - - setAreAllAgentsSelected(false); - setSelectedAgents([]); - } - ); - }} - > - - - )} - - - ) : null} @@ -575,14 +526,6 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { items={totalAgents ? agents : []} itemId="id" columns={columns} - isSelectable={true} - selection={{ - selectable: (agent: Agent) => agent.active, - onSelectionChange: (newSelectedAgents: Agent[]) => { - setSelectedAgents(newSelectedAgents); - setAreAllAgentsSelected(false); - }, - }} pagination={{ pageIndex: pagination.currentPage - 1, pageSize: pagination.pageSize, diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_reassign_config_flyout/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_reassign_config_flyout/index.tsx new file mode 100644 index 0000000000000..11a049047b787 --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_reassign_config_flyout/index.tsx @@ -0,0 +1,186 @@ +/* + * 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, { useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutHeader, + EuiSpacer, + EuiTitle, + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, + EuiButton, + EuiFlyoutFooter, + EuiSelect, + EuiFormRow, + EuiText, + EuiBadge, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { Datasource, Agent } from '../../../../types'; +import { + useGetOneAgentConfig, + sendPutAgentReassign, + useCore, + useGetAgentConfigs, +} from '../../../../hooks'; +import { PackageIcon } from '../../../../components/package_icon'; + +interface Props { + onClose: () => void; + agent: Agent; +} + +export const AgentReassignConfigFlyout: React.FunctionComponent = ({ onClose, agent }) => { + const { notifications } = useCore(); + const [selectedAgentConfigId, setSelectedAgentConfigId] = useState( + agent.config_id + ); + + const agentConfigsRequest = useGetAgentConfigs(); + const agentConfigs = agentConfigsRequest.data ? agentConfigsRequest.data.items : []; + + const agentConfigRequest = useGetOneAgentConfig(selectedAgentConfigId as string); + const agentConfig = agentConfigRequest.data ? agentConfigRequest.data.item : null; + + const [isSubmitting, setIsSubmitting] = useState(false); + + async function onSubmit() { + try { + setIsSubmitting(true); + if (!selectedAgentConfigId) { + throw new Error('No selected config id'); + } + const res = await sendPutAgentReassign(agent.id, { + config_id: selectedAgentConfigId, + }); + if (res.error) { + throw res.error; + } + setIsSubmitting(false); + const successMessage = i18n.translate( + 'xpack.ingestManager.agentReassignConfig.successSingleNotificationTitle', + { + defaultMessage: 'Agent configuration reassigned', + } + ); + notifications.toasts.addSuccess(successMessage); + onClose(); + } catch (error) { + setIsSubmitting(false); + notifications.toasts.addError(error, { + title: 'Unable to reassign agent configuration', + }); + } + } + + return ( + + + +

+ +

+
+ + + + +
+ + + + + ({ + value: config.id, + text: config.name, + }))} + value={selectedAgentConfigId} + onChange={e => setSelectedAgentConfigId(e.target.value)} + /> + + + + + + {agentConfig && ( + + {agentConfig.datasources.length}, + }} + /> + + )} + + {agentConfig && + (agentConfig.datasources as Datasource[]).map((datasource, idx) => { + if (!datasource.package) { + return null; + } + return ( + + + + + + {datasource.package.title} + + + ); + })} + + + + + + + + + + + + + + + +
+ ); +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_unenroll_provider.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_unenroll_provider.tsx index 25499495a7897..fec2253c0dd56 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_unenroll_provider.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_unenroll_provider.tsx @@ -39,7 +39,8 @@ export const AgentUnenrollProvider: React.FunctionComponent = ({ children ) => { if ( agentsToUnenroll === undefined || - (Array.isArray(agentsToUnenroll) && agentsToUnenroll.length === 0) + // !Only supports unenrolling one agent + (Array.isArray(agentsToUnenroll) && agentsToUnenroll.length !== 1) ) { throw new Error('No agents specified for unenrollment'); } @@ -60,55 +61,27 @@ export const AgentUnenrollProvider: React.FunctionComponent = ({ children setIsLoading(true); try { - const unenrollByKuery = typeof agents === 'string'; - const { data, error } = await sendRequest({ - path: agentRouteService.getUnenrollPath(), + const agentId = agents[0]; + const { error } = await sendRequest({ + path: agentRouteService.getUnenrollPath(agentId), method: 'post', - body: JSON.stringify({ - kuery: unenrollByKuery ? agents : undefined, - ids: !unenrollByKuery ? agents : undefined, - }), }); if (error) { throw new Error(error.message); } - const results = data ? data.results : []; - - const successfulResults = results.filter(result => result.success); - const failedResults = results.filter(result => !result.success); - - if (successfulResults.length) { - const hasMultipleSuccesses = successfulResults.length > 1; - const successMessage = hasMultipleSuccesses - ? i18n.translate('xpack.ingestManager.unenrollAgents.successMultipleNotificationTitle', { - defaultMessage: 'Unenrolled {count} agents', - values: { count: successfulResults.length }, - }) - : i18n.translate('xpack.ingestManager.unenrollAgents.successSingleNotificationTitle', { - defaultMessage: "Unenrolled agent '{id}'", - values: { id: successfulResults[0].id }, - }); - core.notifications.toasts.addSuccess(successMessage); - } - - if (failedResults.length) { - const hasMultipleFailures = failedResults.length > 1; - const failureMessage = hasMultipleFailures - ? i18n.translate('xpack.ingestManager.unenrollAgents.failureMultipleNotificationTitle', { - defaultMessage: 'Error unenrolling {count} agents', - values: { count: failedResults.length }, - }) - : i18n.translate('xpack.ingestManager.unenrollAgents.failureSingleNotificationTitle', { - defaultMessage: "Error unenrolling agent '{id}'", - values: { id: failedResults[0].id }, - }); - core.notifications.toasts.addDanger(failureMessage); - } + const successMessage = i18n.translate( + 'xpack.ingestManager.unenrollAgents.successSingleNotificationTitle', + { + defaultMessage: "Unenrolled agent '{id}'", + values: { id: agentId }, + } + ); + core.notifications.toasts.addSuccess(successMessage); if (onSuccessCallback.current) { - onSuccessCallback.current(successfulResults.map(result => result.id)); + onSuccessCallback.current([agentId]); } } catch (e) { core.notifications.toasts.addDanger( diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/index.tsx index 19378fe2fb952..a0092f4073e5a 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/index.tsx @@ -5,5 +5,6 @@ */ export * from './loading'; +export * from './agent_reassign_config_flyout'; export * from './navigation/child_routes'; export * from './navigation/connected_link'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts index 32615278b67d7..75194d3397f90 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts @@ -39,6 +39,8 @@ export { GetOneAgentEventsResponse, GetAgentStatusRequest, GetAgentStatusResponse, + PutAgentReassignRequest, + PutAgentReassignResponse, // API schemas - Enrollment API Keys GetEnrollmentAPIKeysResponse, GetEnrollmentAPIKeysRequest, diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts index 89c827abe30ec..5820303e2a1a7 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts @@ -14,6 +14,7 @@ import { PostAgentEnrollResponse, PostAgentUnenrollResponse, GetAgentStatusResponse, + PutAgentReassignResponse, } from '../../../common/types'; import { GetAgentsRequestSchema, @@ -25,6 +26,7 @@ import { PostAgentEnrollRequestSchema, PostAgentUnenrollRequestSchema, GetAgentStatusRequestSchema, + PutAgentReassignRequestSchema, } from '../../types'; import * as AgentService from '../../services/agents'; import * as APIKeyService from '../../services/api_keys'; @@ -293,60 +295,36 @@ export const getAgentsHandler: RequestHandler< } }; -export const postAgentsUnenrollHandler: RequestHandler< - undefined, +export const postAgentsUnenrollHandler: RequestHandler> = async (context, request, response) => { + const soClient = context.core.savedObjects.client; + try { + await AgentService.unenrollAgent(soClient, request.params.agentId); + + const body: PostAgentUnenrollResponse = { + success: true, + }; + return response.ok({ body }); + } catch (e) { + return response.customError({ + statusCode: 500, + body: { message: e.message }, + }); + } +}; + +export const putAgentsReassignHandler: RequestHandler< + TypeOf, undefined, - TypeOf + TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; try { - const kuery = (request.body as { kuery: string }).kuery; - let toUnenrollIds: string[] = (request.body as { ids: string[] }).ids || []; - - if (kuery) { - let hasMore = true; - let page = 1; - while (hasMore) { - const { agents } = await AgentService.listAgents(soClient, { - page: page++, - perPage: 100, - kuery, - showInactive: true, - }); - if (agents.length === 0) { - hasMore = false; - } - const agentIds = agents.filter(a => a.active).map(a => a.id); - toUnenrollIds = toUnenrollIds.concat(agentIds); - } - } - const results = (await AgentService.unenrollAgents(soClient, toUnenrollIds)).map( - ({ - success, - id, - error, - }): { - success: boolean; - id: string; - action: 'unenrolled'; - error?: { - message: string; - }; - } => { - return { - success, - id, - action: 'unenrolled', - error: error && { - message: error.message, - }, - }; - } - ); + await AgentService.reassignAgent(soClient, request.params.agentId, request.body.config_id); - const body: PostAgentUnenrollResponse = { - results, - success: results.every(result => result.success), + const body: PutAgentReassignResponse = { + success: true, }; return response.ok({ body }); } catch (e) { diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/index.ts b/x-pack/plugins/ingest_manager/server/routes/agent/index.ts index ac27e47db155e..78bb178dce402 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/index.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/index.ts @@ -23,6 +23,7 @@ import { PostAgentUnenrollRequestSchema, GetAgentStatusRequestSchema, PostNewAgentActionRequestSchema, + PutAgentReassignRequestSchema, } from '../../types'; import { getAgentsHandler, @@ -35,6 +36,7 @@ import { postAgentsUnenrollHandler, getAgentStatusForConfigHandler, getInternalUserSOClient, + putAgentsReassignHandler, } from './handlers'; import { postAgentAcksHandlerBuilder } from './acks_handlers'; import * as AgentService from '../../services/agents'; @@ -135,6 +137,15 @@ export const registerRoutes = (router: IRouter) => { postAgentsUnenrollHandler ); + router.put( + { + path: AGENT_API_ROUTES.REASSIGN_PATTERN, + validate: PutAgentReassignRequestSchema, + options: { tags: [`access:${PLUGIN_ID}-all`] }, + }, + putAgentsReassignHandler + ); + // Get agent events router.get( { diff --git a/x-pack/plugins/ingest_manager/server/services/agents/checkin.test.ts b/x-pack/plugins/ingest_manager/server/services/agents/checkin.test.ts index d98052ea87e86..ec10ca6e77e05 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/checkin.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/checkin.test.ts @@ -118,5 +118,19 @@ describe('Agent checkin service', () => { expect(res).toBeTruthy(); }); + + it('should return true if this agent has no revision currently set', () => { + const res = shouldCreateConfigAction( + getAgent({ + config_id: 'config1', + last_checkin: '2018-01-02T00:00:00', + config_revision: null, + config_newest_revision: 2, + }), + [] + ); + + expect(res).toBeTruthy(); + }); }); }); diff --git a/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts b/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts index 9a2b3f22b9431..2873aad7f691a 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts @@ -156,9 +156,13 @@ export function shouldCreateConfigAction(agent: Agent, actions: AgentAction[]): } const isAgentConfigOutdated = - agent.config_revision && - agent.config_newest_revision && - agent.config_revision < agent.config_newest_revision; + // Config reassignment + (!agent.config_revision && agent.config_newest_revision) || + // new revision of a config + (agent.config_revision && + agent.config_newest_revision && + agent.config_revision < agent.config_newest_revision); + if (!isAgentConfigOutdated) { return false; } diff --git a/x-pack/plugins/ingest_manager/server/services/agents/index.ts b/x-pack/plugins/ingest_manager/server/services/agents/index.ts index c95c9ecc2a1d8..257091af0ebd0 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/index.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/index.ts @@ -13,3 +13,4 @@ export * from './status'; export * from './crud'; export * from './update'; export * from './actions'; +export * from './reassign'; diff --git a/x-pack/plugins/ingest_manager/server/services/agents/reassign.ts b/x-pack/plugins/ingest_manager/server/services/agents/reassign.ts new file mode 100644 index 0000000000000..f8142af376eb3 --- /dev/null +++ b/x-pack/plugins/ingest_manager/server/services/agents/reassign.ts @@ -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 { SavedObjectsClientContract } from 'kibana/server'; +import Boom from 'boom'; +import { AGENT_SAVED_OBJECT_TYPE } from '../../constants'; +import { AgentSOAttributes } from '../../types'; +import { agentConfigService } from '../agent_config'; + +export async function reassignAgent( + soClient: SavedObjectsClientContract, + agentId: string, + newConfigId: string +) { + const config = await agentConfigService.get(soClient, newConfigId); + if (!config) { + throw Boom.notFound(`Agent Configuration not found: ${newConfigId}`); + } + + await soClient.update(AGENT_SAVED_OBJECT_TYPE, agentId, { + config_id: newConfigId, + config_revision: null, + config_newest_revision: config.revision, + }); +} diff --git a/x-pack/plugins/ingest_manager/server/services/agents/unenroll.ts b/x-pack/plugins/ingest_manager/server/services/agents/unenroll.ts index 18af9fd4de73f..7f729b0b531ca 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/unenroll.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/unenroll.ts @@ -10,31 +10,7 @@ import { AGENT_SAVED_OBJECT_TYPE } from '../../constants'; import { getAgent } from './crud'; import * as APIKeyService from '../api_keys'; -export async function unenrollAgents( - soClient: SavedObjectsClientContract, - toUnenrollIds: string[] -) { - const response = []; - for (const id of toUnenrollIds) { - try { - await unenrollAgent(soClient, id); - response.push({ - id, - success: true, - }); - } catch (error) { - response.push({ - id, - error, - success: false, - }); - } - } - - return response; -} - -async function unenrollAgent(soClient: SavedObjectsClientContract, agentId: string) { +export async function unenrollAgent(soClient: SavedObjectsClientContract, agentId: string) { const agent = await getAgent(soClient, agentId); await Promise.all([ diff --git a/x-pack/plugins/ingest_manager/server/services/agents/update.ts b/x-pack/plugins/ingest_manager/server/services/agents/update.ts index 59d0ad31d1a64..948e518dff5b4 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/update.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/update.ts @@ -7,7 +7,7 @@ import { SavedObjectsClientContract } from 'src/core/server'; import { listAgents } from './crud'; import { AGENT_SAVED_OBJECT_TYPE } from '../../constants'; -import { unenrollAgents } from './unenroll'; +import { unenrollAgent } from './unenroll'; import { agentConfigService } from '../agent_config'; export async function updateAgentsForConfigId( @@ -55,9 +55,8 @@ export async function unenrollForConfigId(soClient: SavedObjectsClientContract, if (agents.length === 0) { hasMore = false; } - await unenrollAgents( - soClient, - agents.map(a => a.id) - ); + for (const agent of agents) { + await unenrollAgent(soClient, agent.id); + } } } diff --git a/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts b/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts index f94c02ccee40b..ac1679101312e 100644 --- a/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts +++ b/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts @@ -62,14 +62,18 @@ export const PostNewAgentActionRequestSchema = { }; export const PostAgentUnenrollRequestSchema = { - body: schema.oneOf([ - schema.object({ - kuery: schema.string(), - }), - schema.object({ - ids: schema.arrayOf(schema.string()), - }), - ]), + params: schema.object({ + agentId: schema.string(), + }), +}; + +export const PutAgentReassignRequestSchema = { + params: schema.object({ + agentId: schema.string(), + }), + body: schema.object({ + config_id: schema.string(), + }), }; export const GetOneAgentEventsRequestSchema = { diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 68463b2f44259..ba79ab6b9aa83 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -8301,7 +8301,6 @@ "xpack.ingestManager.agentDetails.statusLabel": "ステータス", "xpack.ingestManager.agentDetails.typeLabel": "タイプ", "xpack.ingestManager.agentDetails.unavailableConfigTooltipText": "この構成は利用できなくなりました", - "xpack.ingestManager.agentDetails.unenrollButtonText": "登録解除", "xpack.ingestManager.agentDetails.unexceptedErrorTitle": "エージェントを読み込む間にエラーが発生しました", "xpack.ingestManager.agentDetails.userProvidedMetadataSectionSubtitle": "ユーザー提供メタデータ", "xpack.ingestManager.agentEnrollment.apiKeySelectionDescription": "ご希望のエージェント構成とプラットフォームをすばやく選択できます。次いで、以下の手順に従ってエージェントをセットアップして登録します。", @@ -8333,8 +8332,6 @@ "xpack.ingestManager.agentList.actionsColumnTitle": "アクション", "xpack.ingestManager.agentList.actionsMenuText": "開く", "xpack.ingestManager.agentList.addButton": "新しいエージェントをインストール", - "xpack.ingestManager.agentList.agentsOnPageSelectedMessage": "このページで {count, plural, one {# エージェント} other {# エージェント}}が選択されます。{selectAllLink}", - "xpack.ingestManager.agentList.allAgentsSelectedMessage": "{count} エージェントすべてが選択されます。{clearSelectionLink}", "xpack.ingestManager.agentList.clearFiltersLinkText": "フィルターを消去", "xpack.ingestManager.agentList.configColumnTitle": "構成", "xpack.ingestManager.agentList.configFilterText": "構成", @@ -8346,15 +8343,12 @@ "xpack.ingestManager.agentList.noFilteredAgentsPrompt": "エージェントが見つかりません。{clearFiltersLink}", "xpack.ingestManager.agentList.outOfDateLabel": "最新ではありません", "xpack.ingestManager.agentList.revisionNumber": "rev. {revNumber}", - "xpack.ingestManager.agentList.selectAllAgentsLinkText": "{count} エージェントすべてを選択", - "xpack.ingestManager.agentList.selectPageAgentsLinkText": "このページのみを選択", "xpack.ingestManager.agentList.showInactiveSwitchLabel": "非アクティブエージェントを表示", "xpack.ingestManager.agentList.statusColumnTitle": "ステータス", "xpack.ingestManager.agentList.statusErrorFilterText": "エラー", "xpack.ingestManager.agentList.statusFilterText": "ステータス", "xpack.ingestManager.agentList.statusOfflineFilterText": "オフライン", "xpack.ingestManager.agentList.statusOnlineFilterText": "オンライン", - "xpack.ingestManager.agentList.unenrollButton": "{count, plural, one {# エージェント} other {# エージェント}} の登録を解除", "xpack.ingestManager.agentList.unenrollOneButton": "登録解除", "xpack.ingestManager.agentList.versionTitle": "バージョン", "xpack.ingestManager.agentList.viewActionText": "エージェントの表示", @@ -8502,10 +8496,7 @@ "xpack.ingestManager.unenrollAgents.confirmModal.deleteMultipleTitle": "{count, plural, one {# エージェント} other {# エージェント}}の登録を解除しますか?", "xpack.ingestManager.unenrollAgents.confirmModal.deleteSingleTitle": "エージェント「{id}」の登録を解除しますか?", "xpack.ingestManager.unenrollAgents.confirmModal.loadingButtonLabel": "読み込み中...", - "xpack.ingestManager.unenrollAgents.failureMultipleNotificationTitle": "{count} 件のエージェントの登録解除エラー", - "xpack.ingestManager.unenrollAgents.failureSingleNotificationTitle": "エージェント「{id}」の登録解除エラー", "xpack.ingestManager.unenrollAgents.fatalErrorNotificationTitle": "エージェントの登録解除エラー", - "xpack.ingestManager.unenrollAgents.successMultipleNotificationTitle": "{count} 件のエージェントの登録を解除しました", "xpack.ingestManager.unenrollAgents.successSingleNotificationTitle": "エージェント「{id}」の登録を解除しました", "xpack.ingestManager.yamlConfig.instructionDescription": "この構成でエージェントを登録するには、ホストで次のコマンドをコピーして実行します。", "xpack.ingestManager.yamlConfig.instructionTittle": "フリートに登録", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 0964ab53b7fee..a8d785f47144c 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -8304,7 +8304,6 @@ "xpack.ingestManager.agentDetails.statusLabel": "状态", "xpack.ingestManager.agentDetails.typeLabel": "类型", "xpack.ingestManager.agentDetails.unavailableConfigTooltipText": "此配置不再可用", - "xpack.ingestManager.agentDetails.unenrollButtonText": "取消注册", "xpack.ingestManager.agentDetails.unexceptedErrorTitle": "加载代理时发生错误", "xpack.ingestManager.agentDetails.userProvidedMetadataSectionSubtitle": "用户提供的元数据", "xpack.ingestManager.agentEnrollment.apiKeySelectionDescription": "快速选择所需的代理配置和平台。然后,根据下面的说明设置和注册代理。", @@ -8336,8 +8335,6 @@ "xpack.ingestManager.agentList.actionsColumnTitle": "操作", "xpack.ingestManager.agentList.actionsMenuText": "打开", "xpack.ingestManager.agentList.addButton": "安装新代理", - "xpack.ingestManager.agentList.agentsOnPageSelectedMessage": "已选择此页面上的 {count, plural, one {# 个代理} other {# 个代理}}。{selectAllLink}", - "xpack.ingestManager.agentList.allAgentsSelectedMessage": "已选择所有 {count} 个代理。{clearSelectionLink}", "xpack.ingestManager.agentList.clearFiltersLinkText": "清除筛选", "xpack.ingestManager.agentList.configColumnTitle": "配置", "xpack.ingestManager.agentList.configFilterText": "配置", @@ -8349,15 +8346,12 @@ "xpack.ingestManager.agentList.noFilteredAgentsPrompt": "未找到任何代理。{clearFiltersLink}", "xpack.ingestManager.agentList.outOfDateLabel": "过时", "xpack.ingestManager.agentList.revisionNumber": "修订 {revNumber}", - "xpack.ingestManager.agentList.selectAllAgentsLinkText": "选择所有 {count} 个代理", - "xpack.ingestManager.agentList.selectPageAgentsLinkText": "仅选择此页面", "xpack.ingestManager.agentList.showInactiveSwitchLabel": "显示非活动代理", "xpack.ingestManager.agentList.statusColumnTitle": "状态", "xpack.ingestManager.agentList.statusErrorFilterText": "错误", "xpack.ingestManager.agentList.statusFilterText": "状态", "xpack.ingestManager.agentList.statusOfflineFilterText": "脱机", "xpack.ingestManager.agentList.statusOnlineFilterText": "联机", - "xpack.ingestManager.agentList.unenrollButton": "取消注册 {count, plural, one {# 个代理} other {# 个代理}}", "xpack.ingestManager.agentList.unenrollOneButton": "取消注册", "xpack.ingestManager.agentList.versionTitle": "版本", "xpack.ingestManager.agentList.viewActionText": "查看代理", @@ -8505,10 +8499,7 @@ "xpack.ingestManager.unenrollAgents.confirmModal.deleteMultipleTitle": "取消注册 {count, plural, one {# 个代理} other {# 个代理}}?", "xpack.ingestManager.unenrollAgents.confirmModal.deleteSingleTitle": "取消注册“{id}”?", "xpack.ingestManager.unenrollAgents.confirmModal.loadingButtonLabel": "正在加载……", - "xpack.ingestManager.unenrollAgents.failureMultipleNotificationTitle": "取消注册 {count} 个代理时出错", - "xpack.ingestManager.unenrollAgents.failureSingleNotificationTitle": "取消注册代理“{id}”时出错", "xpack.ingestManager.unenrollAgents.fatalErrorNotificationTitle": "取消注册代理时出错", - "xpack.ingestManager.unenrollAgents.successMultipleNotificationTitle": "已取消注册 {count} 个代理", "xpack.ingestManager.unenrollAgents.successSingleNotificationTitle": "已取消注册代理“{id}”", "xpack.ingestManager.yamlConfig.instructionDescription": "要将代理注册到此配置,请在您的主机上复制并运行以下命令。", "xpack.ingestManager.yamlConfig.instructionTittle": "注册到 fleet", diff --git a/x-pack/test/api_integration/apis/fleet/unenroll_agent.ts b/x-pack/test/api_integration/apis/fleet/unenroll_agent.ts index b484f1f5a8ed2..2acfca63995f1 100644 --- a/x-pack/test/api_integration/apis/fleet/unenroll_agent.ts +++ b/x-pack/test/api_integration/apis/fleet/unenroll_agent.ts @@ -61,50 +61,29 @@ export default function(providerContext: FtrProviderContext) { await esArchiver.unload('fleet/agents'); }); - it('should not allow both ids and kuery in the payload', async () => { - await supertest - .post(`/api/ingest_manager/fleet/agents/unenroll`) - .set('kbn-xsrf', 'xxx') - .send({ - ids: ['agent:1'], - kuery: ['agents.id:1'], - }) - .expect(400); - }); - - it('should not allow no ids or kuery in the payload', async () => { - await supertest - .post(`/api/ingest_manager/fleet/agents/unenroll`) - .set('kbn-xsrf', 'xxx') - .send({}) - .expect(400); - }); - it('allow to unenroll using a list of ids', async () => { const { body } = await supertest - .post(`/api/ingest_manager/fleet/agents/unenroll`) + .post(`/api/ingest_manager/fleet/agents/agent1/unenroll`) .set('kbn-xsrf', 'xxx') .send({ ids: ['agent1'], }) .expect(200); - expect(body).to.have.keys('results', 'success'); + expect(body).to.have.keys('success'); expect(body.success).to.be(true); - expect(body.results).to.have.length(1); - expect(body.results[0].success).to.be(true); }); it('should invalidate related API keys', async () => { const { body } = await supertest - .post(`/api/ingest_manager/fleet/agents/unenroll`) + .post(`/api/ingest_manager/fleet/agents/agent1/unenroll`) .set('kbn-xsrf', 'xxx') .send({ ids: ['agent1'], }) .expect(200); - expect(body).to.have.keys('results', 'success'); + expect(body).to.have.keys('success'); expect(body.success).to.be(true); const { @@ -119,25 +98,5 @@ export default function(providerContext: FtrProviderContext) { expect(outputAPIKeys).length(1); expect(outputAPIKeys[0].invalidated).eql(true); }); - - it('allow to unenroll using a kibana query', async () => { - const { body } = await supertest - .post(`/api/ingest_manager/fleet/agents/unenroll`) - .set('kbn-xsrf', 'xxx') - .send({ - kuery: 'agents.shared_id:agent2_filebeat OR agents.shared_id:agent3_metricbeat', - }) - .expect(200); - - expect(body).to.have.keys('results', 'success'); - expect(body.success).to.be(true); - expect(body.results).to.have.length(2); - expect(body.results[0].success).to.be(true); - - const agentsUnenrolledIds = body.results.map((r: { id: string }) => r.id); - - expect(agentsUnenrolledIds).to.contain('agent2'); - expect(agentsUnenrolledIds).to.contain('agent3'); - }); }); } From 65264aa790f9197efa347a73eb8d8bb48c89e1b1 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Wed, 22 Apr 2020 14:40:09 -0600 Subject: [PATCH 13/39] Set readFromDocValues to false for geo_shape fields (#64014) Co-authored-by: Elastic Machine --- .../should_read_field_from_doc_values.test.ts | 42 +++++++++++++++++++ .../should_read_field_from_doc_values.ts | 2 +- .../es_search_source/es_search_source.js | 2 +- 3 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/should_read_field_from_doc_values.test.ts diff --git a/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/should_read_field_from_doc_values.test.ts b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/should_read_field_from_doc_values.test.ts new file mode 100644 index 0000000000000..784b0b4d4f3d7 --- /dev/null +++ b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/should_read_field_from_doc_values.test.ts @@ -0,0 +1,42 @@ +/* + * 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 { shouldReadFieldFromDocValues } from './should_read_field_from_doc_values'; + +describe('shouldReadFieldFromDocValues', () => { + test('should read field from doc values for aggregatable "number" field', async () => { + expect(shouldReadFieldFromDocValues(true, 'number')).toBe(true); + }); + + test('should not read field from doc values for non-aggregatable "number "field', async () => { + expect(shouldReadFieldFromDocValues(false, 'number')).toBe(false); + }); + + test('should not read field from doc values for "text" field', async () => { + expect(shouldReadFieldFromDocValues(true, 'text')).toBe(false); + }); + + test('should not read field from doc values for "geo_shape" field', async () => { + expect(shouldReadFieldFromDocValues(true, 'geo_shape')).toBe(false); + }); + + test('should not read field from doc values for underscore field', async () => { + expect(shouldReadFieldFromDocValues(true, '_source')).toBe(false); + }); +}); diff --git a/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/should_read_field_from_doc_values.ts b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/should_read_field_from_doc_values.ts index 6d58f7a02c134..56a1cf3ccd161 100644 --- a/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/should_read_field_from_doc_values.ts +++ b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/should_read_field_from_doc_values.ts @@ -18,5 +18,5 @@ */ export function shouldReadFieldFromDocValues(aggregatable: boolean, esType: string) { - return aggregatable && esType !== 'text' && !esType.startsWith('_'); + return aggregatable && !['text', 'geo_shape'].includes(esType) && !esType.startsWith('_'); } diff --git a/x-pack/plugins/maps/public/layers/sources/es_search_source/es_search_source.js b/x-pack/plugins/maps/public/layers/sources/es_search_source/es_search_source.js index 7d3aa86a0fc56..bfbcca1eb3f61 100644 --- a/x-pack/plugins/maps/public/layers/sources/es_search_source/es_search_source.js +++ b/x-pack/plugins/maps/public/layers/sources/es_search_source/es_search_source.js @@ -51,7 +51,7 @@ function getDocValueAndSourceFields(indexPattern, fieldNames) { lang: field.lang, }, }; - } else if (field.type !== ES_GEO_FIELD_TYPE.GEO_SHAPE && field.readFromDocValues) { + } else if (field.readFromDocValues) { const docValueField = field.type === 'date' ? { From f7ea9b99ba61a069013760c8afcd75f22214713b Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Wed, 22 Apr 2020 15:10:39 -0600 Subject: [PATCH 14/39] [Maps] Show spatial filters on map to provide context when for active filters (#63406) * [Maps] show spatial filters * pass data into __dataRequests * extractFeaturesFromFilters * geo_shape support * putting it all together * lower alpha * update removeOrphanedSourcesAndLayers to avoid removing spatialFiltersLayer * change array iteration to forEach * use less precision when distance filter covers larger distances * fix double import * add map settings for to configure spatial filters layer * add map settings alpha slider * finish rest of map settings * review feedback Co-authored-by: Elastic Machine --- x-pack/plugins/maps/common/constants.ts | 2 + .../maps/public/components/alpha_slider.tsx | 46 +++++++ .../layer_settings/layer_settings.js | 35 +---- .../map/mb/draw_control/draw_control.js | 21 ++- .../connected_components/map/mb/index.js | 2 + .../map/mb/mb.utils.test.js | 56 +++----- .../connected_components/map/mb/utils.js | 34 ++++- .../connected_components/map/mb/view.js | 11 +- .../map_settings_panel/map_settings_panel.tsx | 3 + .../spatial_filters_panel.tsx | 98 +++++++++++++ .../maps/public/elasticsearch_geo_utils.js | 40 +++++- .../public/elasticsearch_geo_utils.test.js | 129 ++++++++++++++++++ .../public/reducers/default_map_settings.ts | 4 + x-pack/plugins/maps/public/reducers/map.d.ts | 4 + .../maps/public/selectors/map_selectors.d.ts | 3 + .../maps/public/selectors/map_selectors.js | 66 +++++++-- 16 files changed, 463 insertions(+), 91 deletions(-) create mode 100644 x-pack/plugins/maps/public/components/alpha_slider.tsx create mode 100644 x-pack/plugins/maps/public/connected_components/map_settings_panel/spatial_filters_panel.tsx diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index 6f9c0985f5f4a..fd972219563a8 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -215,3 +215,5 @@ export enum SCALING_TYPES { } export const RGBA_0000 = 'rgba(0,0,0,0)'; + +export const SPATIAL_FILTERS_LAYER_ID = 'SPATIAL_FILTERS_LAYER_ID'; diff --git a/x-pack/plugins/maps/public/components/alpha_slider.tsx b/x-pack/plugins/maps/public/components/alpha_slider.tsx new file mode 100644 index 0000000000000..921c386292050 --- /dev/null +++ b/x-pack/plugins/maps/public/components/alpha_slider.tsx @@ -0,0 +1,46 @@ +/* + * 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 { EuiFormRow } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +// @ts-ignore +import { ValidatedRange } from './validated_range'; + +interface Props { + alpha: number; + onChange: (alpha: number) => void; +} + +export function AlphaSlider({ alpha, onChange }: Props) { + const onAlphaChange = (newAlpha: number) => { + onChange(newAlpha / 100); + }; + + return ( + + + + ); +} diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js index 168c735ab7a6c..d84d05260f982 100644 --- a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js @@ -8,7 +8,7 @@ import React, { Fragment } from 'react'; import { EuiTitle, EuiPanel, EuiFormRow, EuiFieldText, EuiSpacer } from '@elastic/eui'; -import { ValidatedRange } from '../../../components/validated_range'; +import { AlphaSlider } from '../../../components/alpha_slider'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { ValidatedDualRange } from '../../../../../../../src/plugins/kibana_react/public'; @@ -24,8 +24,7 @@ export function LayerSettings(props) { }; const onAlphaChange = alpha => { - const alphaDecimal = alpha / 100; - props.updateAlpha(props.layerId, alphaDecimal); + props.updateAlpha(props.layerId, alpha); }; const renderZoomSliders = () => { @@ -64,34 +63,6 @@ export function LayerSettings(props) { ); }; - const renderAlphaSlider = () => { - const alphaPercent = Math.round(props.alpha * 100); - - return ( - - - - ); - }; - return ( @@ -107,7 +78,7 @@ export function LayerSettings(props) { {renderLabel()} {renderZoomSliders()} - {renderAlphaSlider()} + diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js b/x-pack/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js index d20faa39d6492..a69e06458a6a0 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js @@ -64,13 +64,28 @@ export class DrawControl extends React.Component { if (this.props.drawState.drawType === DRAW_TYPE.DISTANCE) { const circle = e.features[0]; - roundCoordinates(circle.properties.center); + const distanceKm = _.round( + circle.properties.radiusKm, + circle.properties.radiusKm > 10 ? 0 : 2 + ); + // Only include as much precision as needed for distance + let precision = 2; + if (distanceKm <= 1) { + precision = 5; + } else if (distanceKm <= 10) { + precision = 4; + } else if (distanceKm <= 100) { + precision = 3; + } const filter = createDistanceFilterWithMeta({ alias: this.props.drawState.filterLabel, - distanceKm: _.round(circle.properties.radiusKm, circle.properties.radiusKm > 10 ? 0 : 2), + distanceKm, geoFieldName: this.props.drawState.geoFieldName, indexPatternId: this.props.drawState.indexPatternId, - point: circle.properties.center, + point: [ + _.round(circle.properties.center[0], precision), + _.round(circle.properties.center[1], precision), + ], }); this.props.addFilters([filter]); this.props.disableDrawState(); diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/index.js b/x-pack/plugins/maps/public/connected_components/map/mb/index.js index 459b38d422694..f8daf0804265b 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/index.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/index.js @@ -23,6 +23,7 @@ import { isInteractiveDisabled, isTooltipControlDisabled, isViewControlHidden, + getSpatialFiltersLayer, getMapSettings, } from '../../../selectors/map_selectors'; @@ -33,6 +34,7 @@ function mapStateToProps(state = {}) { isMapReady: getMapReady(state), settings: getMapSettings(state), layerList: getLayerList(state), + spatialFiltersLayer: getSpatialFiltersLayer(state), goto: getGoto(state), inspectorAdapters: getInspectorAdapters(state), scrollZoom: getScrollZoom(state), diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/mb.utils.test.js b/x-pack/plugins/maps/public/connected_components/map/mb/mb.utils.test.js index a8c4f61a00da3..4774cdc556c24 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/mb.utils.test.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/mb.utils.test.js @@ -5,6 +5,7 @@ */ import { removeOrphanedSourcesAndLayers, syncLayerOrderForSingleLayer } from './utils'; +import { SPATIAL_FILTERS_LAYER_ID } from '../../../../common/constants'; import _ from 'lodash'; class MockMbMap { @@ -121,7 +122,8 @@ function makeMultiSourceMockLayer(layerId) { ); } -describe('mb/utils', () => { +describe('removeOrphanedSourcesAndLayers', () => { + const spatialFilterLayer = makeMultiSourceMockLayer(SPATIAL_FILTERS_LAYER_ID); test('should remove foo and bar layer', async () => { const bazLayer = makeSingleSourceMockLayer('baz'); const fooLayer = makeSingleSourceMockLayer('foo'); @@ -133,7 +135,7 @@ describe('mb/utils', () => { const currentStyle = getMockStyle(currentLayerList); const mockMbMap = new MockMbMap(currentStyle); - removeOrphanedSourcesAndLayers(mockMbMap, nextLayerList); + removeOrphanedSourcesAndLayers(mockMbMap, nextLayerList, spatialFilterLayer); const removedStyle = mockMbMap.getStyle(); const nextStyle = getMockStyle(nextLayerList); @@ -151,7 +153,7 @@ describe('mb/utils', () => { const currentStyle = getMockStyle(currentLayerList); const mockMbMap = new MockMbMap(currentStyle); - removeOrphanedSourcesAndLayers(mockMbMap, nextLayerList); + removeOrphanedSourcesAndLayers(mockMbMap, nextLayerList, spatialFilterLayer); const removedStyle = mockMbMap.getStyle(); const nextStyle = getMockStyle(nextLayerList); @@ -169,13 +171,23 @@ describe('mb/utils', () => { const currentStyle = getMockStyle(currentLayerList); const mockMbMap = new MockMbMap(currentStyle); - removeOrphanedSourcesAndLayers(mockMbMap, nextLayerList); + removeOrphanedSourcesAndLayers(mockMbMap, nextLayerList, spatialFilterLayer); const removedStyle = mockMbMap.getStyle(); const nextStyle = getMockStyle(nextLayerList); expect(removedStyle).toEqual(nextStyle); }); + test('should not remove spatial filter layer and sources when spatialFilterLayer is provided', async () => { + const styleWithSpatialFilters = getMockStyle([spatialFilterLayer]); + const mockMbMap = new MockMbMap(styleWithSpatialFilters); + + removeOrphanedSourcesAndLayers(mockMbMap, [], spatialFilterLayer); + expect(mockMbMap.getStyle()).toEqual(styleWithSpatialFilters); + }); +}); + +describe('syncLayerOrderForSingleLayer', () => { test('should move bar layer in front of foo layer', async () => { const fooLayer = makeSingleSourceMockLayer('foo'); const barLayer = makeSingleSourceMockLayer('bar'); @@ -250,40 +262,4 @@ describe('mb/utils', () => { const nextStyle = getMockStyle(nextLayerListOrder); expect(orderedStyle).toEqual(nextStyle); }); - - test('should reorder foo and bar and remove baz', async () => { - const bazLayer = makeSingleSourceMockLayer('baz'); - const fooLayer = makeSingleSourceMockLayer('foo'); - const barLayer = makeSingleSourceMockLayer('bar'); - - const currentLayerOrder = [bazLayer, fooLayer, barLayer]; - const nextLayerListOrder = [barLayer, fooLayer]; - - const currentStyle = getMockStyle(currentLayerOrder); - const mockMbMap = new MockMbMap(currentStyle); - removeOrphanedSourcesAndLayers(mockMbMap, nextLayerListOrder); - syncLayerOrderForSingleLayer(mockMbMap, nextLayerListOrder); - const orderedStyle = mockMbMap.getStyle(); - - const nextStyle = getMockStyle(nextLayerListOrder); - expect(orderedStyle).toEqual(nextStyle); - }); - - test('should reorder foo and bar and remove baz, when having multi-source multi-layer data', async () => { - const bazLayer = makeMultiSourceMockLayer('baz'); - const fooLayer = makeSingleSourceMockLayer('foo'); - const barLayer = makeMultiSourceMockLayer('bar'); - - const currentLayerOrder = [bazLayer, fooLayer, barLayer]; - const nextLayerListOrder = [barLayer, fooLayer]; - - const currentStyle = getMockStyle(currentLayerOrder); - const mockMbMap = new MockMbMap(currentStyle); - removeOrphanedSourcesAndLayers(mockMbMap, nextLayerListOrder); - syncLayerOrderForSingleLayer(mockMbMap, nextLayerListOrder); - const orderedStyle = mockMbMap.getStyle(); - - const nextStyle = getMockStyle(nextLayerListOrder); - expect(orderedStyle).toEqual(nextStyle); - }); }); diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/utils.js b/x-pack/plugins/maps/public/connected_components/map/mb/utils.js index 7be2cd9e67084..adf109a087d27 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/utils.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/utils.js @@ -7,11 +7,16 @@ import _ from 'lodash'; import { RGBAImage } from './image_utils'; -export function removeOrphanedSourcesAndLayers(mbMap, layerList) { +export function removeOrphanedSourcesAndLayers(mbMap, layerList, spatialFilterLayer) { const mbStyle = mbMap.getStyle(); const mbLayerIdsToRemove = []; mbStyle.layers.forEach(mbLayer => { + // ignore mapbox layers from spatial filter layer + if (spatialFilterLayer.ownsMbLayerId(mbLayer.id)) { + return; + } + const layer = layerList.find(layer => { return layer.ownsMbLayerId(mbLayer.id); }); @@ -24,6 +29,11 @@ export function removeOrphanedSourcesAndLayers(mbMap, layerList) { const mbSourcesToRemove = []; for (const mbSourceId in mbStyle.sources) { if (mbStyle.sources.hasOwnProperty(mbSourceId)) { + // ignore mapbox sources from spatial filter layer + if (spatialFilterLayer.ownsMbSourceId(mbSourceId)) { + return; + } + const layer = layerList.find(layer => { return layer.ownsMbSourceId(mbSourceId); }); @@ -35,6 +45,21 @@ export function removeOrphanedSourcesAndLayers(mbMap, layerList) { mbSourcesToRemove.forEach(mbSourceId => mbMap.removeSource(mbSourceId)); } +export function moveLayerToTop(mbMap, layer) { + const mbStyle = mbMap.getStyle(); + + if (!mbStyle.layers || mbStyle.layers.length === 0) { + return; + } + + layer.getMbLayerIds().forEach(mbLayerId => { + const mbLayer = mbMap.getLayer(mbLayerId); + if (mbLayer) { + mbMap.moveLayer(mbLayerId); + } + }); +} + /** * This is function assumes only a single layer moved in the layerList, compared to mbMap * It is optimized to minimize the amount of mbMap.moveLayer calls. @@ -47,9 +72,12 @@ export function syncLayerOrderForSingleLayer(mbMap, layerList) { } const mbLayers = mbMap.getStyle().layers.slice(); - const layerIds = mbLayers.map(mbLayer => { + const layerIds = []; + mbLayers.forEach(mbLayer => { const layer = layerList.find(layer => layer.ownsMbLayerId(mbLayer.id)); - return layer.getId(); + if (layer) { + layerIds.push(layer.getId()); + } }); const currentLayerOrderLayerIds = _.uniq(layerIds); diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/view.js b/x-pack/plugins/maps/public/connected_components/map/mb/view.js index 71c1af44e493b..6bb5a4fed6e52 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/view.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/view.js @@ -11,6 +11,7 @@ import { syncLayerOrderForSingleLayer, removeOrphanedSourcesAndLayers, addSpritesheetToMap, + moveLayerToTop, } from './utils'; import { getGlyphUrl, isRetina } from '../../../meta'; import { DECIMAL_DEGREES_PRECISION, ZOOM_PRECISION } from '../../../../common/constants'; @@ -74,7 +75,7 @@ export class MBMapContainer extends React.Component { } _debouncedSync = _.debounce(() => { - if (this._isMounted || !this.props.isMapReady) { + if (this._isMounted && this.props.isMapReady) { if (!this.state.hasSyncedLayerList) { this.setState( { @@ -86,6 +87,7 @@ export class MBMapContainer extends React.Component { } ); } + this.props.spatialFiltersLayer.syncLayerWithMB(this.state.mbMap); this._syncSettings(); } }, 256); @@ -260,9 +262,14 @@ export class MBMapContainer extends React.Component { }; _syncMbMapWithLayerList = () => { - removeOrphanedSourcesAndLayers(this.state.mbMap, this.props.layerList); + removeOrphanedSourcesAndLayers( + this.state.mbMap, + this.props.layerList, + this.props.spatialFiltersLayer + ); this.props.layerList.forEach(layer => layer.syncLayerWithMB(this.state.mbMap)); syncLayerOrderForSingleLayer(this.state.mbMap, this.props.layerList); + moveLayerToTop(this.state.mbMap, this.props.spatialFiltersLayer); }; _syncMbMapWithInspector = () => { diff --git a/x-pack/plugins/maps/public/connected_components/map_settings_panel/map_settings_panel.tsx b/x-pack/plugins/maps/public/connected_components/map_settings_panel/map_settings_panel.tsx index 36ed29e92cf69..a89f4461fff06 100644 --- a/x-pack/plugins/maps/public/connected_components/map_settings_panel/map_settings_panel.tsx +++ b/x-pack/plugins/maps/public/connected_components/map_settings_panel/map_settings_panel.tsx @@ -19,6 +19,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { MapSettings } from '../../reducers/map'; import { NavigationPanel } from './navigation_panel'; +import { SpatialFiltersPanel } from './spatial_filters_panel'; interface Props { cancelChanges: () => void; @@ -60,6 +61,8 @@ export function MapSettingsPanel({
+ +
diff --git a/x-pack/plugins/maps/public/connected_components/map_settings_panel/spatial_filters_panel.tsx b/x-pack/plugins/maps/public/connected_components/map_settings_panel/spatial_filters_panel.tsx new file mode 100644 index 0000000000000..cae703e982966 --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/map_settings_panel/spatial_filters_panel.tsx @@ -0,0 +1,98 @@ +/* + * 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 { EuiFormRow, EuiPanel, EuiSpacer, EuiSwitch, EuiSwitchEvent, EuiTitle } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { MapSettings } from '../../reducers/map'; +import { AlphaSlider } from '../../components/alpha_slider'; +import { MbValidatedColorPicker } from '../../layers/styles/vector/components/color/mb_validated_color_picker'; + +interface Props { + settings: MapSettings; + updateMapSetting: (settingKey: string, settingValue: string | number | boolean) => void; +} + +export function SpatialFiltersPanel({ settings, updateMapSetting }: Props) { + const onAlphaChange = (alpha: number) => { + updateMapSetting('spatialFiltersAlpa', alpha); + }; + + const onFillColorChange = (color: string) => { + updateMapSetting('spatialFiltersFillColor', color); + }; + + const onLineColorChange = (color: string) => { + updateMapSetting('spatialFiltersLineColor', color); + }; + + const onShowSpatialFiltersChange = (event: EuiSwitchEvent) => { + updateMapSetting('showSpatialFilters', event.target.checked); + }; + + const renderStyleInputs = () => { + if (!settings.showSpatialFilters) { + return null; + } + + return ( + <> + + + + + + + + + + + ); + }; + + return ( + + +
+ +
+
+ + + + + + {renderStyleInputs()} +
+ ); +} diff --git a/x-pack/plugins/maps/public/elasticsearch_geo_utils.js b/x-pack/plugins/maps/public/elasticsearch_geo_utils.js index 417c5d84f8916..888fce7e7afe0 100644 --- a/x-pack/plugins/maps/public/elasticsearch_geo_utils.js +++ b/x-pack/plugins/maps/public/elasticsearch_geo_utils.js @@ -18,6 +18,7 @@ import { } from '../common/constants'; import { getEsSpatialRelationLabel } from '../common/i18n_getters'; import { SPATIAL_FILTER_TYPE } from './kibana_services'; +import turfCircle from '@turf/circle'; function ensureGeoField(type) { const expectedTypes = [ES_GEO_FIELD_TYPE.GEO_POINT, ES_GEO_FIELD_TYPE.GEO_SHAPE]; @@ -330,7 +331,7 @@ export function createDistanceFilterWithMeta({ values: { distanceKm, geoFieldName, - pointLabel: point.join(','), + pointLabel: point.join(', '), }, }), }; @@ -451,3 +452,40 @@ export function clamp(val, min, max) { return val; } } + +export function extractFeaturesFromFilters(filters) { + const features = []; + filters + .filter(filter => { + return filter.meta.key && filter.meta.type === SPATIAL_FILTER_TYPE; + }) + .forEach(filter => { + let geometry; + if (filter.geo_distance && filter.geo_distance[filter.meta.key]) { + const distanceSplit = filter.geo_distance.distance.split('km'); + const distance = parseFloat(distanceSplit[0]); + const circleFeature = turfCircle(filter.geo_distance[filter.meta.key], distance); + geometry = circleFeature.geometry; + } else if ( + filter.geo_shape && + filter.geo_shape[filter.meta.key] && + filter.geo_shape[filter.meta.key].shape + ) { + geometry = filter.geo_shape[filter.meta.key].shape; + } else { + // do not know how to convert spatial filter to geometry + // this includes pre-indexed shapes + return; + } + + features.push({ + type: 'Feature', + geometry, + properties: { + filter: filter.meta.alias, + }, + }); + }); + + return features; +} diff --git a/x-pack/plugins/maps/public/elasticsearch_geo_utils.test.js b/x-pack/plugins/maps/public/elasticsearch_geo_utils.test.js index fc02e19173843..d13291a8e2ba5 100644 --- a/x-pack/plugins/maps/public/elasticsearch_geo_utils.test.js +++ b/x-pack/plugins/maps/public/elasticsearch_geo_utils.test.js @@ -19,6 +19,7 @@ import { createExtentFilter, convertMapExtentToPolygon, roundCoordinates, + extractFeaturesFromFilters, } from './elasticsearch_geo_utils'; import { indexPatterns } from '../../../../src/plugins/data/public'; @@ -503,3 +504,131 @@ describe('roundCoordinates', () => { ]); }); }); + +describe('extractFeaturesFromFilters', () => { + it('should ignore non-spatial filers', () => { + const phraseFilter = { + meta: { + alias: null, + disabled: false, + index: '90943e30-9a47-11e8-b64d-95841ca0b247', + key: 'machine.os', + negate: false, + params: { + query: 'ios', + }, + type: 'phrase', + }, + query: { + match_phrase: { + 'machine.os': 'ios', + }, + }, + }; + expect(extractFeaturesFromFilters([phraseFilter])).toEqual([]); + }); + + it('should convert geo_distance filter to feature', () => { + const spatialFilter = { + geo_distance: { + distance: '1096km', + 'geo.coordinates': [-89.87125, 53.49454], + }, + meta: { + alias: 'geo.coordinates within 1096km of -89.87125,53.49454', + disabled: false, + index: '90943e30-9a47-11e8-b64d-95841ca0b247', + key: 'geo.coordinates', + negate: false, + type: 'spatial_filter', + value: '', + }, + }; + + const features = extractFeaturesFromFilters([spatialFilter]); + expect(features[0].geometry.coordinates[0][0]).toEqual([-89.87125, 63.35109118642093]); + expect(features[0].properties).toEqual({ + filter: 'geo.coordinates within 1096km of -89.87125,53.49454', + }); + }); + + it('should convert geo_shape filter to feature', () => { + const spatialFilter = { + geo_shape: { + 'geo.coordinates': { + relation: 'INTERSECTS', + shape: { + coordinates: [ + [ + [-101.21639, 48.1413], + [-101.21639, 41.84905], + [-90.95149, 41.84905], + [-90.95149, 48.1413], + [-101.21639, 48.1413], + ], + ], + type: 'Polygon', + }, + }, + ignore_unmapped: true, + }, + meta: { + alias: 'geo.coordinates in bounds', + disabled: false, + index: '90943e30-9a47-11e8-b64d-95841ca0b247', + key: 'geo.coordinates', + negate: false, + type: 'spatial_filter', + value: '', + }, + }; + + expect(extractFeaturesFromFilters([spatialFilter])).toEqual([ + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [-101.21639, 48.1413], + [-101.21639, 41.84905], + [-90.95149, 41.84905], + [-90.95149, 48.1413], + [-101.21639, 48.1413], + ], + ], + }, + properties: { + filter: 'geo.coordinates in bounds', + }, + }, + ]); + }); + + it('should ignore geo_shape filter with pre-index shape', () => { + const spatialFilter = { + geo_shape: { + 'geo.coordinates': { + indexed_shape: { + id: 's5gldXEBkTB2HMwpC8y0', + index: 'world_countries_v1', + path: 'coordinates', + }, + relation: 'INTERSECTS', + }, + ignore_unmapped: true, + }, + meta: { + alias: 'geo.coordinates in multipolygon', + disabled: false, + index: '90943e30-9a47-11e8-b64d-95841ca0b247', + key: 'geo.coordinates', + negate: false, + type: 'spatial_filter', + value: '', + }, + }; + + expect(extractFeaturesFromFilters([spatialFilter])).toEqual([]); + }); +}); diff --git a/x-pack/plugins/maps/public/reducers/default_map_settings.ts b/x-pack/plugins/maps/public/reducers/default_map_settings.ts index 81622ea9581b0..fe21b37434edd 100644 --- a/x-pack/plugins/maps/public/reducers/default_map_settings.ts +++ b/x-pack/plugins/maps/public/reducers/default_map_settings.ts @@ -11,5 +11,9 @@ export function getDefaultMapSettings(): MapSettings { return { maxZoom: MAX_ZOOM, minZoom: MIN_ZOOM, + showSpatialFilters: true, + spatialFiltersAlpa: 0.3, + spatialFiltersFillColor: '#DA8B45', + spatialFiltersLineColor: '#DA8B45', }; } diff --git a/x-pack/plugins/maps/public/reducers/map.d.ts b/x-pack/plugins/maps/public/reducers/map.d.ts index af2d96eb75562..be0700d4bdd6d 100644 --- a/x-pack/plugins/maps/public/reducers/map.d.ts +++ b/x-pack/plugins/maps/public/reducers/map.d.ts @@ -42,6 +42,10 @@ export type MapContext = { export type MapSettings = { maxZoom: number; minZoom: number; + showSpatialFilters: boolean; + spatialFiltersAlpa: number; + spatialFiltersFillColor: string; + spatialFiltersLineColor: string; }; export type MapState = { diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.d.ts b/x-pack/plugins/maps/public/selectors/map_selectors.d.ts index fed344b7744fe..4d0f652af982a 100644 --- a/x-pack/plugins/maps/public/selectors/map_selectors.d.ts +++ b/x-pack/plugins/maps/public/selectors/map_selectors.d.ts @@ -8,6 +8,7 @@ import { AnyAction } from 'redux'; import { MapCenter } from '../../common/descriptor_types'; import { MapStoreState } from '../reducers/store'; import { MapSettings } from '../reducers/map'; +import { IVectorLayer } from '../layers/vector_layer'; export function getHiddenLayerIds(state: MapStoreState): string[]; @@ -20,3 +21,5 @@ export function getQueryableUniqueIndexPatternIds(state: MapStoreState): string[ export function getMapSettings(state: MapStoreState): MapSettings; export function hasMapSettingsChanges(state: MapStoreState): boolean; + +export function getSpatialFiltersLayer(state: MapStoreState): IVectorLayer; diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.js b/x-pack/plugins/maps/public/selectors/map_selectors.js index 20d1f7e08c910..f43c92d4c9945 100644 --- a/x-pack/plugins/maps/public/selectors/map_selectors.js +++ b/x-pack/plugins/maps/public/selectors/map_selectors.js @@ -6,27 +6,26 @@ import { createSelector } from 'reselect'; import _ from 'lodash'; - import { TileLayer } from '../layers/tile_layer'; - import { VectorTileLayer } from '../layers/vector_tile_layer'; - import { VectorLayer } from '../layers/vector_layer'; - import { HeatmapLayer } from '../layers/heatmap_layer'; - import { BlendedVectorLayer } from '../layers/blended_vector_layer'; - import { getTimeFilter } from '../kibana_services'; - import { getInspectorAdapters } from '../reducers/non_serializable_instances'; import { TiledVectorLayer } from '../layers/tiled_vector_layer'; - import { copyPersistentState, TRACKED_LAYER_DESCRIPTOR } from '../reducers/util'; - import { InnerJoin } from '../layers/joins/inner_join'; - import { getSourceByType } from '../layers/sources/source_registry'; +import { GeojsonFileSource } from '../layers/sources/client_file_source'; +import { + LAYER_TYPE, + SOURCE_DATA_ID_ORIGIN, + STYLE_TYPE, + VECTOR_STYLES, + SPATIAL_FILTERS_LAYER_ID, +} from '../../common/constants'; +import { extractFeaturesFromFilters } from '../elasticsearch_geo_utils'; function createLayerInstance(layerDescriptor, inspectorAdapters) { const source = createSourceInstance(layerDescriptor.sourceDescriptor, inspectorAdapters); @@ -195,6 +194,53 @@ export const getDataFilters = createSelector( } ); +export const getSpatialFiltersLayer = createSelector( + getFilters, + getMapSettings, + (filters, settings) => { + const featureCollection = { + type: 'FeatureCollection', + features: extractFeaturesFromFilters(filters), + }; + const geoJsonSourceDescriptor = GeojsonFileSource.createDescriptor( + featureCollection, + 'spatialFilters' + ); + + return new VectorLayer({ + layerDescriptor: { + id: SPATIAL_FILTERS_LAYER_ID, + visible: settings.showSpatialFilters, + alpha: settings.spatialFiltersAlpa, + type: LAYER_TYPE.VECTOR, + __dataRequests: [ + { + dataId: SOURCE_DATA_ID_ORIGIN, + data: featureCollection, + }, + ], + style: { + properties: { + [VECTOR_STYLES.FILL_COLOR]: { + type: STYLE_TYPE.STATIC, + options: { + color: settings.spatialFiltersFillColor, + }, + }, + [VECTOR_STYLES.LINE_COLOR]: { + type: STYLE_TYPE.STATIC, + options: { + color: settings.spatialFiltersLineColor, + }, + }, + }, + }, + }, + source: new GeojsonFileSource(geoJsonSourceDescriptor), + }); + } +); + export const getLayerList = createSelector( getLayerListRaw, getInspectorAdapters, From dd094f23330a3228176122372812f7e64124d28f Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Wed, 22 Apr 2020 17:36:36 -0400 Subject: [PATCH 15/39] [Lens] Split apart config panel component to more manageable chunks (#63910) * [Lens] Split apart config panel component to more manageable chunks * Moving around and renaming SASS appropriately * Remove layer limit Co-authored-by: Elastic Machine Co-authored-by: cchaos --- .../{ => config_panel}/_chart_switch.scss | 0 .../config_panel/_config_panel.scss | 7 + .../config_panel/_dimension_popover.scss | 9 + .../editor_frame/config_panel/_index.scss | 4 + .../_layer_panel.scss} | 25 +- .../{ => config_panel}/chart_switch.test.tsx | 14 +- .../{ => config_panel}/chart_switch.tsx | 8 +- .../config_panel/config_panel.tsx | 177 +++++ .../config_panel/dimension_popover.tsx | 49 ++ .../editor_frame/config_panel/index.ts | 7 + .../{ => config_panel}/layer_actions.test.ts | 0 .../{ => config_panel}/layer_actions.ts | 4 +- .../editor_frame/config_panel/layer_panel.tsx | 405 +++++++++++ .../config_panel/layer_settings.tsx | 53 ++ .../editor_frame/config_panel/types.ts | 37 + .../editor_frame/config_panel_wrapper.tsx | 655 ------------------ .../editor_frame/editor_frame.tsx | 2 +- .../editor_frame/index.scss | 4 +- .../dimension_panel/dimension_panel.tsx | 2 +- x-pack/plugins/lens/public/types.ts | 93 ++- 20 files changed, 832 insertions(+), 723 deletions(-) rename x-pack/plugins/lens/public/editor_frame_service/editor_frame/{ => config_panel}/_chart_switch.scss (100%) create mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_config_panel.scss create mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_dimension_popover.scss create mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_index.scss rename x-pack/plugins/lens/public/editor_frame_service/editor_frame/{_config_panel_wrapper.scss => config_panel/_layer_panel.scss} (52%) rename x-pack/plugins/lens/public/editor_frame_service/editor_frame/{ => config_panel}/chart_switch.test.tsx (98%) rename x-pack/plugins/lens/public/editor_frame_service/editor_frame/{ => config_panel}/chart_switch.tsx (98%) create mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx create mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.tsx create mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/index.ts rename x-pack/plugins/lens/public/editor_frame_service/editor_frame/{ => config_panel}/layer_actions.test.ts (100%) rename x-pack/plugins/lens/public/editor_frame_service/editor_frame/{ => config_panel}/layer_actions.ts (95%) create mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx create mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.tsx create mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts delete mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel_wrapper.tsx diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_chart_switch.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_chart_switch.scss similarity index 100% rename from x-pack/plugins/lens/public/editor_frame_service/editor_frame/_chart_switch.scss rename to x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_chart_switch.scss diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_config_panel.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_config_panel.scss new file mode 100644 index 0000000000000..1965b51f97034 --- /dev/null +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_config_panel.scss @@ -0,0 +1,7 @@ +.lnsConfigPanel__addLayerBtn { + color: transparentize($euiColorMediumShade, .3); + // Remove EuiButton's default shadow to make button more subtle + // sass-lint:disable-block no-important + box-shadow: none !important; + border: 1px dashed currentColor; +} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_dimension_popover.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_dimension_popover.scss new file mode 100644 index 0000000000000..254807d06d386 --- /dev/null +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_dimension_popover.scss @@ -0,0 +1,9 @@ +.lnsDimensionPopover { + line-height: 0; + flex-grow: 1; +} + +.lnsDimensionPopover__trigger { + max-width: 100%; + display: block; +} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_index.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_index.scss new file mode 100644 index 0000000000000..8f09a358dd5e4 --- /dev/null +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_index.scss @@ -0,0 +1,4 @@ +@import 'chart_switch'; +@import 'config_panel'; +@import 'dimension_popover'; +@import 'layer_panel'; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_config_panel_wrapper.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_layer_panel.scss similarity index 52% rename from x-pack/plugins/lens/public/editor_frame_service/editor_frame/_config_panel_wrapper.scss rename to x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_layer_panel.scss index 62a7f6b023f31..3fbc42f9a25a0 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_config_panel_wrapper.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_layer_panel.scss @@ -1,8 +1,8 @@ -.lnsConfigPanel__panel { +.lnsLayerPanel { margin-bottom: $euiSizeS; } -.lnsConfigPanel__row { +.lnsLayerPanel__row { background: $euiColorLightestShade; padding: $euiSizeS; border-radius: $euiBorderRadius; @@ -13,15 +13,7 @@ } } -.lnsConfigPanel__addLayerBtn { - color: transparentize($euiColorMediumShade, .3); - // Remove EuiButton's default shadow to make button more subtle - // sass-lint:disable-block no-important - box-shadow: none !important; - border: 1px dashed currentColor; -} - -.lnsConfigPanel__dimension { +.lnsLayerPanel__dimension { @include euiFontSizeS; background: lightOrDarkTheme($euiColorEmptyShade, $euiColorLightestShade); border-radius: $euiBorderRadius; @@ -31,12 +23,7 @@ overflow: hidden; } -.lnsConfigPanel__trigger { - max-width: 100%; - display: block; -} - -.lnsConfigPanel__triggerLink { +.lnsLayerPanel__triggerLink { padding: $euiSizeS; width: 100%; display: flex; @@ -44,7 +31,3 @@ min-height: $euiSizeXXL; } -.lnsConfigPanel__popover { - line-height: 0; - flex-grow: 1; -} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/chart_switch.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch.test.tsx similarity index 98% rename from x-pack/plugins/lens/public/editor_frame_service/editor_frame/chart_switch.test.tsx rename to x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch.test.tsx index 87c359eb1345b..3c61d270b1bcf 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/chart_switch.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch.test.tsx @@ -5,13 +5,17 @@ */ import React from 'react'; -import { createMockVisualization, createMockFramePublicAPI, createMockDatasource } from '../mocks'; -import { mountWithIntl as mount } from 'test_utils/enzyme_helpers'; import { ReactWrapper } from 'enzyme'; -import { ChartSwitch } from './chart_switch'; -import { Visualization, FramePublicAPI, DatasourcePublicAPI } from '../../types'; +import { + createMockVisualization, + createMockFramePublicAPI, + createMockDatasource, +} from '../../mocks'; import { EuiKeyPadMenuItem } from '@elastic/eui'; -import { Action } from './state_management'; +import { mountWithIntl as mount } from 'test_utils/enzyme_helpers'; +import { Visualization, FramePublicAPI, DatasourcePublicAPI } from '../../../types'; +import { Action } from '../state_management'; +import { ChartSwitch } from './chart_switch'; describe('chart_switch', () => { function generateVisualization(id: string): jest.Mocked { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/chart_switch.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch.tsx similarity index 98% rename from x-pack/plugins/lens/public/editor_frame_service/editor_frame/chart_switch.tsx rename to x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch.tsx index 6bf7983d32023..1461449f3c1c8 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/chart_switch.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch.tsx @@ -15,10 +15,10 @@ import { } from '@elastic/eui'; import { flatten } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { Visualization, FramePublicAPI, Datasource } from '../../types'; -import { Action } from './state_management'; -import { getSuggestions, switchToSuggestion, Suggestion } from './suggestion_helpers'; -import { trackUiEvent } from '../../lens_ui_telemetry'; +import { Visualization, FramePublicAPI, Datasource } from '../../../types'; +import { Action } from '../state_management'; +import { getSuggestions, switchToSuggestion, Suggestion } from '../suggestion_helpers'; +import { trackUiEvent } from '../../../lens_ui_telemetry'; interface VisualizationSelection { visualizationId: string; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx new file mode 100644 index 0000000000000..e5d3e93258c0a --- /dev/null +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx @@ -0,0 +1,177 @@ +/* + * 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, memo } from 'react'; +import { EuiFlexItem, EuiToolTip, EuiButton, EuiForm } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { Visualization } from '../../../types'; +import { ChartSwitch } from './chart_switch'; +import { LayerPanel } from './layer_panel'; +import { trackUiEvent } from '../../../lens_ui_telemetry'; +import { generateId } from '../../../id_generator'; +import { removeLayer, appendLayer } from './layer_actions'; +import { ConfigPanelWrapperProps } from './types'; + +export const ConfigPanelWrapper = memo(function ConfigPanelWrapper(props: ConfigPanelWrapperProps) { + const activeVisualization = props.visualizationMap[props.activeVisualizationId || '']; + const { visualizationState } = props; + + return ( + <> + + {activeVisualization && visualizationState && ( + + )} + + ); +}); + +function LayerPanels( + props: ConfigPanelWrapperProps & { + activeDatasourceId: string; + activeVisualization: Visualization; + } +) { + const { + framePublicAPI, + activeVisualization, + visualizationState, + dispatch, + activeDatasourceId, + datasourceMap, + } = props; + const setVisualizationState = useMemo( + () => (newState: unknown) => { + props.dispatch({ + type: 'UPDATE_VISUALIZATION_STATE', + visualizationId: activeVisualization.id, + newState, + clearStagedPreview: false, + }); + }, + [props.dispatch, activeVisualization] + ); + const updateDatasource = useMemo( + () => (datasourceId: string, newState: unknown) => { + props.dispatch({ + type: 'UPDATE_DATASOURCE_STATE', + updater: () => newState, + datasourceId, + clearStagedPreview: false, + }); + }, + [props.dispatch] + ); + const updateAll = useMemo( + () => (datasourceId: string, newDatasourceState: unknown, newVisualizationState: unknown) => { + props.dispatch({ + type: 'UPDATE_STATE', + subType: 'UPDATE_ALL_STATES', + updater: prevState => { + return { + ...prevState, + datasourceStates: { + ...prevState.datasourceStates, + [datasourceId]: { + state: newDatasourceState, + isLoading: false, + }, + }, + visualization: { + ...prevState.visualization, + state: newVisualizationState, + }, + stagedPreview: undefined, + }; + }, + }); + }, + [props.dispatch] + ); + const layerIds = activeVisualization.getLayerIds(visualizationState); + + return ( + + {layerIds.map(layerId => ( + { + dispatch({ + type: 'UPDATE_STATE', + subType: 'REMOVE_OR_CLEAR_LAYER', + updater: state => + removeLayer({ + activeVisualization, + layerId, + trackUiEvent, + datasourceMap, + state, + }), + }); + }} + /> + ))} + {activeVisualization.appendLayer && visualizationState && ( + + + { + dispatch({ + type: 'UPDATE_STATE', + subType: 'ADD_LAYER', + updater: state => + appendLayer({ + activeVisualization, + generateId, + trackUiEvent, + activeDatasource: datasourceMap[activeDatasourceId], + state, + }), + }); + }} + iconType="plusInCircleFilled" + /> + + + )} + + ); +} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.tsx new file mode 100644 index 0000000000000..36db13b74ac4f --- /dev/null +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.tsx @@ -0,0 +1,49 @@ +/* + * 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 { EuiPopover } from '@elastic/eui'; +import { VisualizationDimensionGroupConfig } from '../../../types'; +import { DimensionPopoverState } from './types'; + +export function DimensionPopover({ + popoverState, + setPopoverState, + groups, + accessor, + groupId, + trigger, + panel, +}: { + popoverState: DimensionPopoverState; + setPopoverState: (newState: DimensionPopoverState) => void; + groups: VisualizationDimensionGroupConfig[]; + accessor: string; + groupId: string; + trigger: React.ReactElement; + panel: React.ReactElement; +}) { + const noMatch = popoverState.isOpen ? !groups.some(d => d.accessors.includes(accessor)) : false; + return ( + { + setPopoverState({ isOpen: false, openId: null, addingToGroupId: null }); + }} + button={trigger} + anchorPosition="leftUp" + withTitle + panelPaddingSize="s" + > + {panel} + + ); +} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/index.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/index.ts new file mode 100644 index 0000000000000..754b3fb5c6fde --- /dev/null +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/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 { ConfigPanelWrapper } from './config_panel'; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/layer_actions.test.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions.test.ts similarity index 100% rename from x-pack/plugins/lens/public/editor_frame_service/editor_frame/layer_actions.test.ts rename to x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions.test.ts diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/layer_actions.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions.ts similarity index 95% rename from x-pack/plugins/lens/public/editor_frame_service/editor_frame/layer_actions.ts rename to x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions.ts index cc2cbb172d23e..3d1d590664238 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/layer_actions.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions.ts @@ -5,8 +5,8 @@ */ import _ from 'lodash'; -import { EditorFrameState } from './state_management'; -import { Datasource, Visualization } from '../../types'; +import { EditorFrameState } from '../state_management'; +import { Datasource, Visualization } from '../../../types'; interface RemoveLayerOptions { trackUiEvent: (name: string) => void; 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 new file mode 100644 index 0000000000000..f7be82dd34ba3 --- /dev/null +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -0,0 +1,405 @@ +/* + * 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, { useContext, useState } from 'react'; +import { + EuiPanel, + EuiSpacer, + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, + EuiFormRow, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { NativeRenderer } from '../../../native_renderer'; +import { Visualization, FramePublicAPI, StateSetter } from '../../../types'; +import { DragContext, DragDrop, ChildDragDropProvider } from '../../../drag_drop'; +import { LayerSettings } from './layer_settings'; +import { trackUiEvent } from '../../../lens_ui_telemetry'; +import { generateId } from '../../../id_generator'; +import { ConfigPanelWrapperProps, DimensionPopoverState } from './types'; +import { DimensionPopover } from './dimension_popover'; + +export function LayerPanel( + props: Exclude & { + frame: FramePublicAPI; + layerId: string; + isOnlyLayer: boolean; + activeVisualization: Visualization; + visualizationState: unknown; + updateVisualization: StateSetter; + updateDatasource: (datasourceId: string, newState: unknown) => void; + updateAll: ( + datasourceId: string, + newDatasourcestate: unknown, + newVisualizationState: unknown + ) => void; + onRemoveLayer: () => void; + } +) { + const dragDropContext = useContext(DragContext); + const { framePublicAPI, layerId, activeVisualization, isOnlyLayer, onRemoveLayer } = props; + const datasourcePublicAPI = framePublicAPI.datasourceLayers[layerId]; + if (!datasourcePublicAPI) { + return null; + } + const layerVisualizationConfigProps = { + layerId, + dragDropContext, + state: props.visualizationState, + frame: props.framePublicAPI, + dateRange: props.framePublicAPI.dateRange, + }; + const datasourceId = datasourcePublicAPI.datasourceId; + const layerDatasourceState = props.datasourceStates[datasourceId].state; + const layerDatasource = props.datasourceMap[datasourceId]; + + const layerDatasourceDropProps = { + layerId, + dragDropContext, + state: layerDatasourceState, + setState: (newState: unknown) => { + props.updateDatasource(datasourceId, newState); + }, + }; + + const layerDatasourceConfigProps = { + ...layerDatasourceDropProps, + frame: props.framePublicAPI, + dateRange: props.framePublicAPI.dateRange, + }; + + const [popoverState, setPopoverState] = useState({ + isOpen: false, + openId: null, + addingToGroupId: null, + }); + + const { groups } = activeVisualization.getConfiguration(layerVisualizationConfigProps); + const isEmptyLayer = !groups.some(d => d.accessors.length > 0); + + return ( + + + + + + + + {layerDatasource && ( + + { + const newState = + typeof updater === 'function' ? updater(layerDatasourceState) : updater; + // Look for removed columns + const nextPublicAPI = layerDatasource.getPublicAPI({ + state: newState, + layerId, + dateRange: props.framePublicAPI.dateRange, + }); + const nextTable = new Set( + nextPublicAPI.getTableSpec().map(({ columnId }) => columnId) + ); + const removed = datasourcePublicAPI + .getTableSpec() + .map(({ columnId }) => columnId) + .filter(columnId => !nextTable.has(columnId)); + let nextVisState = props.visualizationState; + removed.forEach(columnId => { + nextVisState = activeVisualization.removeDimension({ + layerId, + columnId, + prevState: nextVisState, + }); + }); + + props.updateAll(datasourceId, newState, nextVisState); + }, + }} + /> + + )} + + + + + {groups.map((group, index) => { + const newId = generateId(); + const isMissing = !isEmptyLayer && group.required && group.accessors.length === 0; + return ( + + <> + {group.accessors.map(accessor => ( + { + layerDatasource.onDrop({ + ...layerDatasourceDropProps, + droppedItem, + columnId: accessor, + filterOperations: group.filterOperations, + }); + }} + > + { + if (popoverState.isOpen) { + setPopoverState({ + isOpen: false, + openId: null, + addingToGroupId: null, + }); + } else { + setPopoverState({ + isOpen: true, + openId: accessor, + addingToGroupId: null, // not set for existing dimension + }); + } + }, + }} + /> + } + panel={ + + } + /> + + { + trackUiEvent('indexpattern_dimension_removed'); + props.updateAll( + datasourceId, + layerDatasource.removeColumn({ + layerId, + columnId: accessor, + prevState: layerDatasourceState, + }), + props.activeVisualization.removeDimension({ + layerId, + columnId: accessor, + prevState: props.visualizationState, + }) + ); + }} + /> + + ))} + {group.supportsMoreColumns ? ( + { + const dropSuccess = layerDatasource.onDrop({ + ...layerDatasourceDropProps, + droppedItem, + columnId: newId, + filterOperations: group.filterOperations, + }); + if (dropSuccess) { + props.updateVisualization( + activeVisualization.setDimension({ + layerId, + groupId: group.groupId, + columnId: newId, + prevState: props.visualizationState, + }) + ); + } + }} + > + + { + if (popoverState.isOpen) { + setPopoverState({ + isOpen: false, + openId: null, + addingToGroupId: null, + }); + } else { + setPopoverState({ + isOpen: true, + openId: newId, + addingToGroupId: group.groupId, + }); + } + }} + size="xs" + > + + +
+ } + panel={ + { + props.updateAll( + datasourceId, + newState, + activeVisualization.setDimension({ + layerId, + groupId: group.groupId, + columnId: newId, + prevState: props.visualizationState, + }) + ); + setPopoverState({ + isOpen: true, + openId: newId, + addingToGroupId: null, // clear now that dimension exists + }); + }, + }} + /> + } + /> + + ) : null} + + + ); + })} + + + + + + { + // If we don't blur the remove / clear button, it remains focused + // which is a strange UX in this case. e.target.blur doesn't work + // due to who knows what, but probably event re-writing. Additionally, + // activeElement does not have blur so, we need to do some casting + safeguards. + const el = (document.activeElement as unknown) as { blur: () => void }; + + if (el?.blur) { + el.blur(); + } + + onRemoveLayer(); + }} + > + {isOnlyLayer + ? i18n.translate('xpack.lens.resetLayer', { + defaultMessage: 'Reset layer', + }) + : i18n.translate('xpack.lens.deleteLayer', { + defaultMessage: 'Delete layer', + })} + + + + + + ); +} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.tsx new file mode 100644 index 0000000000000..57588e31590b4 --- /dev/null +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.tsx @@ -0,0 +1,53 @@ +/* + * 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, { useState } from 'react'; +import { EuiPopover, EuiButtonIcon } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { NativeRenderer } from '../../../native_renderer'; +import { Visualization, VisualizationLayerWidgetProps } from '../../../types'; + +export function LayerSettings({ + layerId, + activeVisualization, + layerConfigProps, +}: { + layerId: string; + activeVisualization: Visualization; + layerConfigProps: VisualizationLayerWidgetProps; +}) { + const [isOpen, setIsOpen] = useState(false); + + if (!activeVisualization.renderLayerContextMenu) { + return null; + } + + return ( + setIsOpen(!isOpen)} + data-test-subj="lns_layer_settings" + /> + } + isOpen={isOpen} + closePopover={() => setIsOpen(false)} + anchorPosition="leftUp" + > + + + ); +} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts new file mode 100644 index 0000000000000..df510d3648f8c --- /dev/null +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts @@ -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 { Action } from '../state_management'; +import { + Visualization, + FramePublicAPI, + Datasource, + DatasourceDimensionEditorProps, +} from '../../../types'; + +export interface ConfigPanelWrapperProps { + activeDatasourceId: string; + visualizationState: unknown; + visualizationMap: Record; + activeVisualizationId: string | null; + dispatch: (action: Action) => void; + framePublicAPI: FramePublicAPI; + datasourceMap: Record; + datasourceStates: Record< + string, + { + isLoading: boolean; + state: unknown; + } + >; + core: DatasourceDimensionEditorProps['core']; +} + +export interface DimensionPopoverState { + isOpen: boolean; + openId: string | null; + addingToGroupId: string | null; +} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel_wrapper.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel_wrapper.tsx deleted file mode 100644 index da812e948b23f..0000000000000 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel_wrapper.tsx +++ /dev/null @@ -1,655 +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, { useMemo, useContext, memo, useState } from 'react'; -import { - EuiPanel, - EuiSpacer, - EuiPopover, - EuiButtonIcon, - EuiFlexGroup, - EuiFlexItem, - EuiButtonEmpty, - EuiToolTip, - EuiButton, - EuiForm, - EuiFormRow, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { NativeRenderer } from '../../native_renderer'; -import { Action } from './state_management'; -import { - Visualization, - FramePublicAPI, - Datasource, - VisualizationLayerWidgetProps, - DatasourceDimensionEditorProps, - StateSetter, -} from '../../types'; -import { DragContext, DragDrop, ChildDragDropProvider } from '../../drag_drop'; -import { ChartSwitch } from './chart_switch'; -import { trackUiEvent } from '../../lens_ui_telemetry'; -import { generateId } from '../../id_generator'; -import { removeLayer, appendLayer } from './layer_actions'; - -interface ConfigPanelWrapperProps { - activeDatasourceId: string; - visualizationState: unknown; - visualizationMap: Record; - activeVisualizationId: string | null; - dispatch: (action: Action) => void; - framePublicAPI: FramePublicAPI; - datasourceMap: Record; - datasourceStates: Record< - string, - { - isLoading: boolean; - state: unknown; - } - >; - core: DatasourceDimensionEditorProps['core']; -} - -export const ConfigPanelWrapper = memo(function ConfigPanelWrapper(props: ConfigPanelWrapperProps) { - const activeVisualization = props.visualizationMap[props.activeVisualizationId || '']; - const { visualizationState } = props; - - return ( - <> - - {activeVisualization && visualizationState && ( - - )} - - ); -}); - -function LayerPanels( - props: ConfigPanelWrapperProps & { - activeDatasourceId: string; - activeVisualization: Visualization; - } -) { - const { - framePublicAPI, - activeVisualization, - visualizationState, - dispatch, - activeDatasourceId, - datasourceMap, - } = props; - const setVisualizationState = useMemo( - () => (newState: unknown) => { - props.dispatch({ - type: 'UPDATE_VISUALIZATION_STATE', - visualizationId: activeVisualization.id, - newState, - clearStagedPreview: false, - }); - }, - [props.dispatch, activeVisualization] - ); - const updateDatasource = useMemo( - () => (datasourceId: string, newState: unknown) => { - props.dispatch({ - type: 'UPDATE_DATASOURCE_STATE', - updater: () => newState, - datasourceId, - clearStagedPreview: false, - }); - }, - [props.dispatch] - ); - const updateAll = useMemo( - () => (datasourceId: string, newDatasourceState: unknown, newVisualizationState: unknown) => { - props.dispatch({ - type: 'UPDATE_STATE', - subType: 'UPDATE_ALL_STATES', - updater: prevState => { - return { - ...prevState, - datasourceStates: { - ...prevState.datasourceStates, - [datasourceId]: { - state: newDatasourceState, - isLoading: false, - }, - }, - visualization: { - ...prevState.visualization, - state: newVisualizationState, - }, - stagedPreview: undefined, - }; - }, - }); - }, - [props.dispatch] - ); - const layerIds = activeVisualization.getLayerIds(visualizationState); - - return ( - - {layerIds.map(layerId => ( - { - dispatch({ - type: 'UPDATE_STATE', - subType: 'REMOVE_OR_CLEAR_LAYER', - updater: state => - removeLayer({ - activeVisualization, - layerId, - trackUiEvent, - datasourceMap, - state, - }), - }); - }} - /> - ))} - {activeVisualization.appendLayer && ( - - - { - dispatch({ - type: 'UPDATE_STATE', - subType: 'ADD_LAYER', - updater: state => - appendLayer({ - activeVisualization, - generateId, - trackUiEvent, - activeDatasource: datasourceMap[activeDatasourceId], - state, - }), - }); - }} - iconType="plusInCircleFilled" - /> - - - )} - - ); -} - -function LayerPanel( - props: Exclude & { - frame: FramePublicAPI; - layerId: string; - isOnlyLayer: boolean; - activeVisualization: Visualization; - visualizationState: unknown; - updateVisualization: StateSetter; - updateDatasource: (datasourceId: string, newState: unknown) => void; - updateAll: ( - datasourceId: string, - newDatasourcestate: unknown, - newVisualizationState: unknown - ) => void; - onRemoveLayer: () => void; - } -) { - const dragDropContext = useContext(DragContext); - const { framePublicAPI, layerId, activeVisualization, isOnlyLayer, onRemoveLayer } = props; - const datasourcePublicAPI = framePublicAPI.datasourceLayers[layerId]; - if (!datasourcePublicAPI) { - return null; - } - const layerVisualizationConfigProps = { - layerId, - dragDropContext, - state: props.visualizationState, - frame: props.framePublicAPI, - dateRange: props.framePublicAPI.dateRange, - }; - const datasourceId = datasourcePublicAPI.datasourceId; - const layerDatasourceState = props.datasourceStates[datasourceId].state; - const layerDatasource = props.datasourceMap[datasourceId]; - - const layerDatasourceDropProps = { - layerId, - dragDropContext, - state: layerDatasourceState, - setState: (newState: unknown) => { - props.updateDatasource(datasourceId, newState); - }, - }; - - const layerDatasourceConfigProps = { - ...layerDatasourceDropProps, - frame: props.framePublicAPI, - dateRange: props.framePublicAPI.dateRange, - }; - - const [popoverState, setPopoverState] = useState<{ - isOpen: boolean; - openId: string | null; - addingToGroupId: string | null; - }>({ - isOpen: false, - openId: null, - addingToGroupId: null, - }); - - const { groups } = activeVisualization.getConfiguration(layerVisualizationConfigProps); - const isEmptyLayer = !groups.some(d => d.accessors.length > 0); - - function wrapInPopover( - id: string, - groupId: string, - trigger: React.ReactElement, - panel: React.ReactElement - ) { - const noMatch = popoverState.isOpen ? !groups.some(d => d.accessors.includes(id)) : false; - return ( - { - setPopoverState({ isOpen: false, openId: null, addingToGroupId: null }); - }} - button={trigger} - anchorPosition="leftUp" - withTitle - panelPaddingSize="s" - > - {panel} - - ); - } - - return ( - - - - - - - - {layerDatasource && ( - - { - const newState = - typeof updater === 'function' ? updater(layerDatasourceState) : updater; - // Look for removed columns - const nextPublicAPI = layerDatasource.getPublicAPI({ - state: newState, - layerId, - dateRange: props.framePublicAPI.dateRange, - }); - const nextTable = new Set( - nextPublicAPI.getTableSpec().map(({ columnId }) => columnId) - ); - const removed = datasourcePublicAPI - .getTableSpec() - .map(({ columnId }) => columnId) - .filter(columnId => !nextTable.has(columnId)); - let nextVisState = props.visualizationState; - removed.forEach(columnId => { - nextVisState = activeVisualization.removeDimension({ - layerId, - columnId, - prevState: nextVisState, - }); - }); - - props.updateAll(datasourceId, newState, nextVisState); - }, - }} - /> - - )} - - - - - {groups.map((group, index) => { - const newId = generateId(); - const isMissing = !isEmptyLayer && group.required && group.accessors.length === 0; - return ( - - <> - {group.accessors.map(accessor => ( - { - layerDatasource.onDrop({ - ...layerDatasourceDropProps, - droppedItem, - columnId: accessor, - filterOperations: group.filterOperations, - }); - }} - > - {wrapInPopover( - accessor, - group.groupId, - { - if (popoverState.isOpen) { - setPopoverState({ - isOpen: false, - openId: null, - addingToGroupId: null, - }); - } else { - setPopoverState({ - isOpen: true, - openId: accessor, - addingToGroupId: null, // not set for existing dimension - }); - } - }, - }} - />, - - )} - - { - trackUiEvent('indexpattern_dimension_removed'); - props.updateAll( - datasourceId, - layerDatasource.removeColumn({ - layerId, - columnId: accessor, - prevState: layerDatasourceState, - }), - props.activeVisualization.removeDimension({ - layerId, - columnId: accessor, - prevState: props.visualizationState, - }) - ); - }} - /> - - ))} - {group.supportsMoreColumns ? ( - { - const dropSuccess = layerDatasource.onDrop({ - ...layerDatasourceDropProps, - droppedItem, - columnId: newId, - filterOperations: group.filterOperations, - }); - if (dropSuccess) { - props.updateVisualization( - activeVisualization.setDimension({ - layerId, - groupId: group.groupId, - columnId: newId, - prevState: props.visualizationState, - }) - ); - } - }} - > - {wrapInPopover( - newId, - group.groupId, -
- { - if (popoverState.isOpen) { - setPopoverState({ - isOpen: false, - openId: null, - addingToGroupId: null, - }); - } else { - setPopoverState({ - isOpen: true, - openId: newId, - addingToGroupId: group.groupId, - }); - } - }} - size="xs" - > - - -
, - { - props.updateAll( - datasourceId, - newState, - activeVisualization.setDimension({ - layerId, - groupId: group.groupId, - columnId: newId, - prevState: props.visualizationState, - }) - ); - setPopoverState({ - isOpen: true, - openId: newId, - addingToGroupId: null, // clear now that dimension exists - }); - }, - }} - /> - )} -
- ) : null} - -
- ); - })} - - - - - - { - // If we don't blur the remove / clear button, it remains focused - // which is a strange UX in this case. e.target.blur doesn't work - // due to who knows what, but probably event re-writing. Additionally, - // activeElement does not have blur so, we need to do some casting + safeguards. - const el = (document.activeElement as unknown) as { blur: () => void }; - - if (el?.blur) { - el.blur(); - } - - onRemoveLayer(); - }} - > - {isOnlyLayer - ? i18n.translate('xpack.lens.resetLayer', { - defaultMessage: 'Reset layer', - }) - : i18n.translate('xpack.lens.deleteLayer', { - defaultMessage: 'Delete layer', - })} - - - -
-
- ); -} - -function LayerSettings({ - layerId, - activeVisualization, - layerConfigProps, -}: { - layerId: string; - activeVisualization: Visualization; - layerConfigProps: VisualizationLayerWidgetProps; -}) { - const [isOpen, setIsOpen] = useState(false); - - if (!activeVisualization.renderLayerContextMenu) { - return null; - } - - return ( - setIsOpen(!isOpen)} - data-test-subj="lns_layer_settings" - /> - } - isOpen={isOpen} - closePopover={() => setIsOpen(false)} - anchorPosition="leftUp" - > - - - ); -} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx index b72d9081bbc91..5cd803e7cebbc 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx @@ -16,7 +16,7 @@ import { } from '../../types'; import { reducer, getInitialState } from './state_management'; import { DataPanelWrapper } from './data_panel_wrapper'; -import { ConfigPanelWrapper } from './config_panel_wrapper'; +import { ConfigPanelWrapper } from './config_panel'; import { FrameLayout } from './frame_layout'; import { SuggestionPanel } from './suggestion_panel'; import { WorkspacePanel } from './workspace_panel'; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/index.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/index.scss index d4b27c6c98b3c..5e3726c953f11 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/index.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/index.scss @@ -1,8 +1,6 @@ -@import 'chart_switch'; -@import 'config_panel_wrapper'; +@import 'config_panel/index'; @import 'data_panel_wrapper'; @import 'expression_renderer'; @import 'frame_layout'; @import 'suggestion_panel'; @import 'workspace_panel_wrapper'; - 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 b3bd08d3bbfbe..583832aafcbe8 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 @@ -192,7 +192,7 @@ export const IndexPatternDimensionTriggerComponent = function IndexPatternDimens return ( { props.togglePopover(); }} diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 0b432c0c70727..181f192520d0d 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -273,7 +273,7 @@ export type VisualizationLayerWidgetProps = VisualizationConfigProp setState: (newState: T) => void; }; -type VisualizationDimensionGroupConfig = SharedDimensionProps & { +export type VisualizationDimensionGroupConfig = SharedDimensionProps & { groupLabel: string; /** ID is passed back to visualization. For example, `x` */ @@ -368,55 +368,86 @@ export interface VisualizationType { } export interface Visualization { + /** Plugin ID, such as "lnsXY" */ id: string; + /** + * Initialize is allowed to modify the state stored in memory. The initialize function + * is called with a previous state in two cases: + * - Loadingn from a saved visualization + * - When using suggestions, the suggested state is passed in + */ + initialize: (frame: FramePublicAPI, state?: P) => T; + /** + * Can remove any state that should not be persisted to saved object, such as UI state + */ + getPersistableState: (state: T) => P; + + /** + * Visualizations must provide at least one type for the chart switcher, + * but can register multiple subtypes + */ visualizationTypes: VisualizationType[]; + /** + * If the visualization has subtypes, update the subtype in state. + */ + switchVisualizationType?: (visualizationTypeId: string, state: T) => T; + /** Description is displayed as the clickable text in the chart switcher */ + getDescription: (state: T) => { icon?: IconType; label: string }; + /** Frame needs to know which layers the visualization is currently using */ getLayerIds: (state: T) => string[]; + /** Reset button on each layer triggers this */ clearLayer: (state: T, layerId: string) => T; + /** Optional, if the visualization supports multiple layers */ removeLayer?: (state: T, layerId: string) => T; + /** Track added layers in internal state */ appendLayer?: (state: T, layerId: string) => T; - // Layer context menu is used by visualizations for styling the entire layer - // For example, the XY visualization uses this to have multiple chart types - getLayerContextMenuIcon?: (opts: { state: T; layerId: string }) => IconType | undefined; - renderLayerContextMenu?: (domElement: Element, props: VisualizationLayerWidgetProps) => void; - + /** + * For consistency across different visualizations, the dimension configuration UI is standardized + */ getConfiguration: ( props: VisualizationConfigProps ) => { groups: VisualizationDimensionGroupConfig[] }; - getDescription: ( - state: T - ) => { - icon?: IconType; - label: string; - }; - - switchVisualizationType?: (visualizationTypeId: string, state: T) => T; - - // For initializing from saved object - initialize: (frame: FramePublicAPI, state?: P) => T; - - getPersistableState: (state: T) => P; + /** + * Popover contents that open when the user clicks the contextMenuIcon. This can be used + * for extra configurability, such as for styling the legend or axis + */ + renderLayerContextMenu?: (domElement: Element, props: VisualizationLayerWidgetProps) => void; + /** + * Visualizations can provide a custom icon which will open a layer-specific popover + * If no icon is provided, gear icon is default + */ + getLayerContextMenuIcon?: (opts: { state: T; layerId: string }) => IconType | undefined; - // Actions triggered by the frame which tell the datasource that a dimension is being changed - setDimension: ( - props: VisualizationDimensionChangeProps & { - groupId: string; - } - ) => T; + /** + * The frame is telling the visualization to update or set a dimension based on user interaction + * groupId is coming from the groupId provided in getConfiguration + */ + setDimension: (props: VisualizationDimensionChangeProps & { groupId: string }) => T; + /** + * The frame is telling the visualization to remove a dimension. The visualization needs to + * look at its internal state to determine which dimension is being affected. + */ removeDimension: (props: VisualizationDimensionChangeProps) => T; - toExpression: (state: T, frame: FramePublicAPI) => Ast | string | null; + /** + * The frame will call this function on all visualizations at different times. The + * main use cases where visualization suggestions are requested are: + * - When dragging a field + * - When opening the chart switcher + * If the state is provided when requesting suggestions, the visualization is active. + * Most visualizations will apply stricter filtering to suggestions when they are active, + * because suggestions have the potential to remove the users's work in progress. + */ + getSuggestions: (context: SuggestionRequest) => Array>; + toExpression: (state: T, frame: FramePublicAPI) => Ast | string | null; /** - * Epression to render a preview version of the chart in very constraint space. + * Expression to render a preview version of the chart in very constrained space. * If there is no expression provided, the preview icon is used. */ toPreviewExpression?: (state: T, frame: FramePublicAPI) => Ast | string | null; - - // The frame will call this function on all visualizations when the table changes, or when - // rendering additional ways of using the data - getSuggestions: (context: SuggestionRequest) => Array>; } From 4fc1c5f5cbdad4b1321048a64692050b18f4d140 Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Wed, 22 Apr 2020 17:42:38 -0400 Subject: [PATCH 16/39] [search source] Stop building invalid queries and then rewriting them (#60954) * Stop rewriting invalid filters in search source * Fix type issue * Use automated fixing tool * Update test * Fix test * Fix docs Co-authored-by: Elastic Machine --- .../aggs/buckets/create_filter/filters.ts | 2 +- .../public/search/aggs/buckets/filters.ts | 2 +- .../search/search_source/search_source.ts | 20 ------------------- 3 files changed, 2 insertions(+), 22 deletions(-) diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/filters.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/filters.ts index 1999b759a23d0..72d2029a12b0d 100644 --- a/src/plugins/data/public/search/aggs/buckets/create_filter/filters.ts +++ b/src/plugins/data/public/search/aggs/buckets/create_filter/filters.ts @@ -28,6 +28,6 @@ export const createFilterFilters = (aggConfig: IBucketAggConfig, key: string) => const indexPattern = aggConfig.getIndexPattern(); if (filter && indexPattern && indexPattern.id) { - return buildQueryFilter(filter.query, indexPattern.id, key); + return buildQueryFilter(filter, indexPattern.id, key); } }; diff --git a/src/plugins/data/public/search/aggs/buckets/filters.ts b/src/plugins/data/public/search/aggs/buckets/filters.ts index a42cb70a62b7d..fe013928bba65 100644 --- a/src/plugins/data/public/search/aggs/buckets/filters.ts +++ b/src/plugins/data/public/search/aggs/buckets/filters.ts @@ -107,7 +107,7 @@ export const getFiltersBucketAgg = ({ (typeof filter.input.query === 'string' ? filter.input.query : toAngularJSON(filter.input.query)); - filters[label] = { query }; + filters[label] = query; }, {} ); diff --git a/src/plugins/data/public/search/search_source/search_source.ts b/src/plugins/data/public/search/search_source/search_source.ts index c9fecb3f7e9ce..091a27a6f418d 100644 --- a/src/plugins/data/public/search/search_source/search_source.ts +++ b/src/plugins/data/public/search/search_source/search_source.ts @@ -447,26 +447,6 @@ export class SearchSource { delete searchRequest.highlightAll; } - const translateToQuery = (filter: Filter) => filter && (filter.query || filter); - - // re-write filters within filter aggregations - (function recurse(aggBranch) { - if (!aggBranch) return; - Object.keys(aggBranch).forEach(function(id) { - const agg = aggBranch[id]; - - if (agg.filters) { - // translate filters aggregations - const { filters: aggFilters } = agg.filters; - Object.keys(aggFilters).forEach(filterId => { - aggFilters[filterId] = translateToQuery(aggFilters[filterId]); - }); - } - - recurse(agg.aggs || agg.aggregations); - }); - })(body.aggs || body.aggregations); - return searchRequest; } From 1a0988f96438fb5c20e10cc46fd672ffc4e57c45 Mon Sep 17 00:00:00 2001 From: Catherine Liu Date: Wed, 22 Apr 2020 21:06:49 -0700 Subject: [PATCH 17/39] [Canvas] Top Menu (#59982) * Redesigned workpad_header to top menu layout * Added comments * Fixed element spec * Removed element preview images * Removed tooltip from menus * Removed extraneous JSX * Fixed element fixtures * Moved component strings * top menu design tweaks * Added filter debug element * Fix file picker in asset manager * Sort components strings object keys * Removed ElementTypes component in favor of SavedElementsModal * Updated stories * Fixed custom elements functional tests * Removed unused tag strings * Fixed test fixtures * Updated element_menu stories * Updated view_menu stories * TS for SavedElementsModal * Updated types * Fixed TS errors * Fix i18n errors * Renamed stories * Fixed test file name * Fixed stories * Updated storyshots * Reverted storybook webpack config change * Fixed SavedElementsModal stories * Updated comments * Removed unnecessary ts-ignores * Moved workpad_shortcuts back to /components * Unskip custom elements functional test * Reverted workpad_loader changes * Added element_menu stories and mocks * Fixed element i18n strings * Updated storyshots * Updated storyshot Co-authored-by: Ryan Keairns Co-authored-by: Elastic Machine --- .../canvas/.storybook/storyshots.test.js | 14 +- .../canvas/.storybook/webpack.config.js | 4 +- .../elements/area_chart/header.png | Bin 152584 -> 0 bytes .../elements/area_chart/index.ts | 7 +- .../elements/bubble_chart/header.png | Bin 68804 -> 0 bytes .../elements/bubble_chart/index.ts | 7 +- .../elements/debug/header.png | Bin 14991 -> 0 bytes .../canvas_plugin_src/elements/debug/index.ts | 6 +- .../elements/donut/header.png | Bin 32480 -> 0 bytes .../canvas_plugin_src/elements/donut/index.ts | 21 - .../elements/dropdown_filter/header.png | Bin 16623 -> 0 bytes .../elements/dropdown_filter/index.ts | 9 +- .../elements/filter_debug/index.ts | 16 + .../elements/horizontal_bar_chart/header.png | Bin 19052 -> 0 bytes .../elements/horizontal_bar_chart/index.ts | 7 +- .../horizontal_progress_bar/header.png | Bin 12740 -> 0 bytes .../elements/horizontal_progress_bar/index.ts | 6 +- .../horizontal_progress_pill/header.png | Bin 19006 -> 0 bytes .../horizontal_progress_pill/index.ts | 6 +- .../elements/image/header.png | Bin 93226 -> 0 bytes .../canvas_plugin_src/elements/image/index.ts | 5 +- .../canvas_plugin_src/elements/index.ts | 6 +- .../elements/line_chart/header.png | Bin 59575 -> 0 bytes .../elements/line_chart/index.ts | 7 +- .../elements/markdown/header.png | Bin 33733 -> 0 bytes .../elements/markdown/index.ts | 10 +- .../elements/metric/header.png | Bin 12631 -> 0 bytes .../elements/metric/index.ts | 6 +- .../canvas_plugin_src/elements/pie/header.png | Bin 12580 -> 0 bytes .../canvas_plugin_src/elements/pie/index.ts | 8 +- .../elements/plot/header.png | Bin 37595 -> 0 bytes .../canvas_plugin_src/elements/plot/index.ts | 4 +- .../elements/progress_gauge/header.png | Bin 22905 -> 0 bytes .../elements/progress_gauge/index.ts | 7 +- .../elements/progress_semicircle/header.png | Bin 16999 -> 0 bytes .../elements/progress_semicircle/index.ts | 6 +- .../elements/progress_wheel/header.png | Bin 26408 -> 0 bytes .../elements/progress_wheel/index.ts | 6 +- .../elements/repeat_image/header.png | Bin 83775 -> 0 bytes .../elements/repeat_image/index.ts | 4 +- .../elements/reveal_image/header.png | Bin 8989 -> 0 bytes .../elements/reveal_image/index.ts | 4 +- .../elements/shape/header.png | Bin 10388 -> 0 bytes .../canvas_plugin_src/elements/shape/index.ts | 5 +- .../elements/table/header.png | Bin 16125 -> 0 bytes .../canvas_plugin_src/elements/table/index.ts | 5 +- .../elements/tilted_pie/header.png | Bin 33235 -> 0 bytes .../elements/tilted_pie/index.ts | 23 - .../elements/time_filter/header.png | Bin 18970 -> 0 bytes .../elements/time_filter/index.ts | 7 +- .../elements/vert_bar_chart/header.png | Bin 17393 -> 0 bytes .../elements/vert_bar_chart/index.ts | 5 +- .../elements/vertical_progress_bar/header.png | Bin 15098 -> 0 bytes .../elements/vertical_progress_bar/index.ts | 4 +- .../vertical_progress_pill/header.png | Bin 20611 -> 0 bytes .../elements/vertical_progress_pill/index.ts | 4 +- .../canvas_plugin_src/uis/tags/chart.ts | 15 - .../canvas_plugin_src/uis/tags/filter.ts | 16 - .../canvas_plugin_src/uis/tags/graphic.ts | 15 - .../canvas_plugin_src/uis/tags/index.ts | 7 +- .../canvas_plugin_src/uis/tags/proportion.ts | 15 - .../canvas/canvas_plugin_src/uis/tags/text.ts | 13 - .../plugins/canvas/common/lib/constants.ts | 1 + .../legacy/plugins/canvas/i18n/components.ts | 292 ++++-- .../canvas/i18n/elements/apply_strings.ts | 13 - .../i18n/elements/element_strings.test.ts | 13 - .../canvas/i18n/elements/element_strings.ts | 58 +- .../i18n/functions/dict/alter_column.ts | 2 +- x-pack/legacy/plugins/canvas/i18n/tags.ts | 20 - .../apps/workpad/workpad_app/workpad_app.scss | 2 +- .../asset_manager.stories.storyshot | 761 ++++++++++++++ ...examples.tsx => asset_manager.stories.tsx} | 3 +- .../asset_manager/asset_manager.tsx | 25 +- .../components/asset_manager/asset_modal.tsx | 1 + .../element_grid.stories.storyshot | 797 --------------- .../components/element_types/element_grid.tsx | 110 --- .../components/element_types/element_types.js | 224 ----- .../public/components/element_types/index.js | 103 -- .../canvas/public/components/popover/index.ts | 2 +- .../public/components/popover/popover.tsx | 4 +- .../element_controls.stories.storyshot | 4 +- .../element_grid.stories.storyshot | 333 +++++++ .../saved_elements_modal.stories.storyshot | 933 ++++++++++++++++++ .../__examples__/element_controls.stories.tsx | 2 +- .../__examples__/element_grid.stories.tsx | 31 +- .../__examples__/fixtures/test_elements.tsx | 35 - .../saved_elements_modal.stories.tsx | 50 + .../element_controls.tsx | 8 +- .../saved_elements_modal/element_grid.tsx | 81 ++ .../components/saved_elements_modal/index.ts | 128 +++ .../saved_elements_modal.tsx | 217 ++++ .../sidebar_header.stories.storyshot | 104 +- .../__examples__/sidebar_header.stories.tsx | 4 +- .../sidebar_header/sidebar_header.tsx | 16 +- .../control_settings/control_settings.tsx | 10 +- .../element_menu.examples.storyshot | 41 + .../__examples__/element_menu.examples.tsx | 150 +++ .../element_menu/element_menu.scss | 3 + .../element_menu/element_menu.tsx | 216 ++++ .../workpad_header/element_menu/index.tsx | 49 + .../__snapshots__/pdf_panel.stories.storyshot | 4 +- .../share_menu.examples.storyshot | 36 + .../__examples__/pdf_panel.stories.tsx | 2 +- .../__examples__/share_menu.examples.tsx} | 6 +- .../share_website_flyout.stories.tsx | 2 +- .../flyout/index.ts | 10 +- .../flyout/runtime_step.tsx | 0 .../flyout/share_website_flyout.tsx | 2 +- .../flyout/snippets_step.tsx | 2 +- .../flyout/workpad_step.tsx | 0 .../{workpad_export => share_menu}/index.ts | 15 +- .../pdf_panel.tsx | 4 +- .../share_menu.scss} | 4 +- .../share_menu.tsx} | 51 +- .../utils.test.ts | 0 .../{workpad_export => share_menu}/utils.ts | 0 .../__snapshots__/view_menu.stories.storyshot | 67 ++ .../__examples__/view_menu.stories.tsx | 39 + .../workpad_header/view_menu/index.ts | 85 ++ .../view_menu/lib/get_fit_zoom_scale.ts | 39 + .../workpad_header/view_menu/view_menu.tsx | 172 ++++ .../workpad_export.examples.storyshot | 41 - .../workpad_header/workpad_header.tsx | 233 ++--- .../workpad_header/workpad_zoom/index.tsx | 38 - .../workpad_zoom/workpad_zoom.scss | 11 - .../workpad_zoom/workpad_zoom.tsx | 176 ---- .../public/lib/custom_element_service.ts | 4 +- .../canvas/public/lib/default_header.png | Bin 623 -> 0 bytes .../plugins/canvas/public/lib/element.ts | 22 +- .../canvas/public/lib/flatten_panel_tree.ts | 23 + .../canvas/public/state/selectors/workpad.ts | 9 +- .../plugins/canvas/public/style/index.scss | 3 +- .../test/workpads/austin.json | 4 +- .../tasks/mocks/customElementService.js | 43 + .../proportion.js => tasks/mocks/uiMetric.js} | 5 +- x-pack/legacy/plugins/canvas/types/canvas.ts | 7 + .../legacy/plugins/canvas/types/elements.ts | 8 +- .../routes/custom_elements/update.test.ts | 1 - .../translations/translations/ja-JP.json | 84 +- .../translations/translations/zh-CN.json | 84 +- .../functional/apps/canvas/custom_elements.ts | 54 +- .../functional/page_objects/canvas_page.ts | 6 +- 142 files changed, 4160 insertions(+), 2392 deletions(-) delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/area_chart/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/bubble_chart/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/debug/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/donut/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/donut/index.ts delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/dropdown_filter/header.png create mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/filter_debug/index.ts delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/horizontal_bar_chart/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/horizontal_progress_bar/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/horizontal_progress_pill/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/image/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/line_chart/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/markdown/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/metric/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/pie/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/plot/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/progress_gauge/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/progress_semicircle/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/progress_wheel/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/repeat_image/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/reveal_image/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/shape/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/table/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/tilted_pie/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/tilted_pie/index.ts delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/time_filter/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/vert_bar_chart/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/vertical_progress_bar/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/vertical_progress_pill/header.png delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/chart.ts delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/filter.ts delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/graphic.ts delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/proportion.ts delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/text.ts create mode 100644 x-pack/legacy/plugins/canvas/public/components/asset_manager/__examples__/__snapshots__/asset_manager.stories.storyshot rename x-pack/legacy/plugins/canvas/public/components/asset_manager/__examples__/{asset_manager.examples.tsx => asset_manager.stories.tsx} (97%) delete mode 100644 x-pack/legacy/plugins/canvas/public/components/element_types/__examples__/__snapshots__/element_grid.stories.storyshot delete mode 100644 x-pack/legacy/plugins/canvas/public/components/element_types/element_grid.tsx delete mode 100644 x-pack/legacy/plugins/canvas/public/components/element_types/element_types.js delete mode 100644 x-pack/legacy/plugins/canvas/public/components/element_types/index.js rename x-pack/legacy/plugins/canvas/public/components/{element_types => saved_elements_modal}/__examples__/__snapshots__/element_controls.stories.storyshot (88%) create mode 100644 x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/__examples__/__snapshots__/element_grid.stories.storyshot create mode 100644 x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/__examples__/__snapshots__/saved_elements_modal.stories.storyshot rename x-pack/legacy/plugins/canvas/public/components/{element_types => saved_elements_modal}/__examples__/element_controls.stories.tsx (90%) rename x-pack/legacy/plugins/canvas/public/components/{element_types => saved_elements_modal}/__examples__/element_grid.stories.tsx (53%) rename x-pack/legacy/plugins/canvas/public/components/{element_types => saved_elements_modal}/__examples__/fixtures/test_elements.tsx (93%) create mode 100644 x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/__examples__/saved_elements_modal.stories.tsx rename x-pack/legacy/plugins/canvas/public/components/{element_types => saved_elements_modal}/element_controls.tsx (85%) create mode 100644 x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/element_grid.tsx create mode 100644 x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/index.ts create mode 100644 x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/saved_elements_modal.tsx create mode 100644 x-pack/legacy/plugins/canvas/public/components/workpad_header/element_menu/__examples__/__snapshots__/element_menu.examples.storyshot create mode 100644 x-pack/legacy/plugins/canvas/public/components/workpad_header/element_menu/__examples__/element_menu.examples.tsx create mode 100644 x-pack/legacy/plugins/canvas/public/components/workpad_header/element_menu/element_menu.scss create mode 100644 x-pack/legacy/plugins/canvas/public/components/workpad_header/element_menu/element_menu.tsx create mode 100644 x-pack/legacy/plugins/canvas/public/components/workpad_header/element_menu/index.tsx rename x-pack/legacy/plugins/canvas/public/components/workpad_header/{workpad_export => share_menu}/__examples__/__snapshots__/pdf_panel.stories.storyshot (93%) create mode 100644 x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/__examples__/__snapshots__/share_menu.examples.storyshot rename x-pack/legacy/plugins/canvas/public/components/workpad_header/{workpad_export => share_menu}/__examples__/pdf_panel.stories.tsx (92%) rename x-pack/legacy/plugins/canvas/public/components/workpad_header/{workpad_export/__examples__/workpad_export.examples.tsx => share_menu/__examples__/share_menu.examples.tsx} (79%) rename x-pack/legacy/plugins/canvas/public/components/workpad_header/{workpad_export => share_menu}/flyout/__examples__/share_website_flyout.stories.tsx (93%) rename x-pack/legacy/plugins/canvas/public/components/workpad_header/{workpad_export => share_menu}/flyout/index.ts (91%) rename x-pack/legacy/plugins/canvas/public/components/workpad_header/{workpad_export => share_menu}/flyout/runtime_step.tsx (100%) rename x-pack/legacy/plugins/canvas/public/components/workpad_header/{workpad_export => share_menu}/flyout/share_website_flyout.tsx (98%) rename x-pack/legacy/plugins/canvas/public/components/workpad_header/{workpad_export => share_menu}/flyout/snippets_step.tsx (98%) rename x-pack/legacy/plugins/canvas/public/components/workpad_header/{workpad_export => share_menu}/flyout/workpad_step.tsx (100%) rename x-pack/legacy/plugins/canvas/public/components/workpad_header/{workpad_export => share_menu}/index.ts (86%) rename x-pack/legacy/plugins/canvas/public/components/workpad_header/{workpad_export => share_menu}/pdf_panel.tsx (92%) rename x-pack/legacy/plugins/canvas/public/components/workpad_header/{workpad_export/workpad_export.scss => share_menu/share_menu.scss} (61%) rename x-pack/legacy/plugins/canvas/public/components/workpad_header/{workpad_export/workpad_export.tsx => share_menu/share_menu.tsx} (71%) rename x-pack/legacy/plugins/canvas/public/components/workpad_header/{workpad_export => share_menu}/utils.test.ts (100%) rename x-pack/legacy/plugins/canvas/public/components/workpad_header/{workpad_export => share_menu}/utils.ts (100%) create mode 100644 x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/__examples__/__snapshots__/view_menu.stories.storyshot create mode 100644 x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/__examples__/view_menu.stories.tsx create mode 100644 x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/index.ts create mode 100644 x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/lib/get_fit_zoom_scale.ts create mode 100644 x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/view_menu.tsx delete mode 100644 x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/__examples__/__snapshots__/workpad_export.examples.storyshot delete mode 100644 x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_zoom/index.tsx delete mode 100644 x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_zoom/workpad_zoom.scss delete mode 100644 x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_zoom/workpad_zoom.tsx delete mode 100644 x-pack/legacy/plugins/canvas/public/lib/default_header.png create mode 100644 x-pack/legacy/plugins/canvas/public/lib/flatten_panel_tree.ts create mode 100644 x-pack/legacy/plugins/canvas/tasks/mocks/customElementService.js rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/uis/tags/proportion.js => tasks/mocks/uiMetric.js} (57%) diff --git a/x-pack/legacy/plugins/canvas/.storybook/storyshots.test.js b/x-pack/legacy/plugins/canvas/.storybook/storyshots.test.js index 72b0b8f0e533f..a81483d1e7a17 100644 --- a/x-pack/legacy/plugins/canvas/.storybook/storyshots.test.js +++ b/x-pack/legacy/plugins/canvas/.storybook/storyshots.test.js @@ -7,6 +7,7 @@ import path from 'path'; import moment from 'moment'; import 'moment-timezone'; +import ReactDOM from "react-dom"; import initStoryshots, { multiSnapshotWithOptions } from '@storybook/addon-storyshots'; import styleSheetSerializer from 'jest-styled-components/src/styleSheetSerializer'; @@ -24,6 +25,9 @@ moment.tz.setDefault('UTC'); const testTime = new Date(Date.UTC(2019, 5, 1)); // June 1 2019 Date.now = jest.fn(() => testTime); +// Mock telemetry service +jest.mock('../public/lib/ui_metric', () => ({ trackCanvasUiMetric: () => { } })); + // Mock EUI generated ids to be consistently predictable for snapshots. jest.mock(`@elastic/eui/lib/components/form/form_row/make_id`, () => () => `generated-id`); @@ -32,7 +36,7 @@ jest.mock(`@elastic/eui/lib/components/form/form_row/make_id`, () => () => `gene jest.mock('@elastic/eui/lib/components/code/code', () => { const React = require.requireActual('react'); return { - EuiCode: ({children, className}) => ( + EuiCode: ({ children, className }) => ( {children} @@ -61,6 +65,12 @@ jest.mock('@elastic/eui/packages/react-datepicker', () => { }; }); + +// Mock React Portal for components that use modals, tooltips, etc +ReactDOM.createPortal = jest.fn((element) => { + return element; +}); + jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => { return { htmlIdGenerator: () => () => `generated-id`, @@ -71,7 +81,7 @@ jest.mock('plugins/interpreter/registries', () => ({})); // Disabling this test due to https://github.com/elastic/eui/issues/2242 jest.mock( - '../public/components/workpad_header/workpad_export/flyout/__examples__/share_website_flyout.stories', + '../public/components/workpad_header/share_menu/flyout/__examples__/share_website_flyout.stories', () => { return 'Disabled Panel'; } diff --git a/x-pack/legacy/plugins/canvas/.storybook/webpack.config.js b/x-pack/legacy/plugins/canvas/.storybook/webpack.config.js index cc74faeac6a96..963cf831ef698 100644 --- a/x-pack/legacy/plugins/canvas/.storybook/webpack.config.js +++ b/x-pack/legacy/plugins/canvas/.storybook/webpack.config.js @@ -177,8 +177,10 @@ module.exports = async ({ config }) => { }), // Mock out libs used by a few componets to avoid loading in kibana_legacy and platform - new webpack.NormalModuleReplacementPlugin(/lib\/notify/, path.resolve(__dirname, '../tasks/mocks/uiNotify')), + new webpack.NormalModuleReplacementPlugin(/(lib)?\/notify/, path.resolve(__dirname, '../tasks/mocks/uiNotify')), new webpack.NormalModuleReplacementPlugin(/lib\/download_workpad/, path.resolve(__dirname, '../tasks/mocks/downloadWorkpad')), + new webpack.NormalModuleReplacementPlugin(/(lib)?\/custom_element_service/, path.resolve(__dirname, '../tasks/mocks/customElementService')), + new webpack.NormalModuleReplacementPlugin(/(lib)?\/ui_metric/, path.resolve(__dirname, '../tasks/mocks/uiMetric')), ); // Tell Webpack about relevant extensions diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/area_chart/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/area_chart/header.png deleted file mode 100644 index 93456066429d903dd0bd2d267f89ea92e72c6cdb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 152584 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 G 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�UORMj)CHED< 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 D@A8 ({ name: 'areaChart', - displayName: 'Area chart', + displayName: 'Area', help: 'A line chart with a filled body', - tags: ['chart'], - image: header, + type: 'chart', + icon: 'visArea', expression: `filters | demodata | pointseries x="time" y="mean(price)" diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/bubble_chart/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/bubble_chart/header.png deleted file mode 100644 index db541fe7c53b8f6c2c95c4f1cdd396ad6477ebea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68804 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

c+PfXAw>FCf8z6F@RgtS9xbtKxbg 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

eJK@iJhSDKy4!90oT$9dIWkJZcJ>pZ3#>L{ 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 diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/bubble_chart/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/bubble_chart/index.ts index 7ab510e419769..7ac1d0ac83b0b 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/bubble_chart/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/bubble_chart/index.ts @@ -5,16 +5,15 @@ */ import { ElementFactory } from '../../../types'; -import header from './header.png'; export const bubbleChart: ElementFactory = () => ({ name: 'bubbleChart', - displayName: 'Bubble chart', - tags: ['chart'], + displayName: 'Bubble', + type: 'chart', help: 'A customizable bubble chart', width: 700, height: 300, - image: header, + icon: 'heatmap', expression: `filters | demodata | pointseries x="project" y="sum(price)" color="state" size="size(username)" diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/debug/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/debug/header.png deleted file mode 100644 index 37ab329a49bb8b7b5ab152a93e2ed7495e82ad23..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14991 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 ({ name: 'debug', - displayName: 'Debug', - tags: ['text'], + displayName: 'Debug data', help: 'Just dumps the configuration of the element', - image: header, + icon: 'bug', expression: `demodata | render as=debug`, }); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/donut/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/donut/header.png deleted file mode 100644 index 4bbfb6f8f68fca38579ec0f91bae212223472e88..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32480 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≪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 diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/donut/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/donut/index.ts deleted file mode 100644 index 4ea8037d2073e..0000000000000 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/donut/index.ts +++ /dev/null @@ -1,21 +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 { ElementFactory } from '../../../types'; -import header from './header.png'; - -export const donut: ElementFactory = () => ({ - name: 'donut', - displayName: 'Donut chart', - tags: ['chart', 'proportion'], - help: 'A customizable donut chart', - image: header, - expression: `filters -| demodata -| pointseries color="project" size="max(price)" -| pie hole=50 labels=false legend="ne" -| render`, -}); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/dropdown_filter/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/dropdown_filter/header.png deleted file mode 100644 index 727b4d23941fdbd5adec8c92a0b3a4d50e19f779..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16623 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 ({ - name: 'dropdown_filter', - displayName: 'Dropdown filter', - tags: ['filter'], + name: 'dropdownFilter', + displayName: 'Dropdown select', + type: 'filter', help: 'A dropdown from which you can select values for an "exactly" filter', - image: header, + icon: 'filter', height: 50, expression: `demodata | dropdownControl valueColumn=project filterColumn=project | render`, diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/filter_debug/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/filter_debug/index.ts new file mode 100644 index 0000000000000..35a4a75f49c4e --- /dev/null +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/filter_debug/index.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 { ElementFactory } from '../../../types'; + +export const filterDebug: ElementFactory = () => ({ + name: 'filterDebug', + displayName: 'Debug filter', + help: 'Shows the underlying global filters in a workpad', + icon: 'bug', + expression: `filters +| render as=debug`, +}); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/horizontal_bar_chart/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/horizontal_bar_chart/header.png deleted file mode 100644 index 9b6ee47d88698fa786efc6baf1de7de1032437b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19052 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 a{2Todk6}$EAHYZTD9 ({ name: 'horizontalBarChart', - displayName: 'Horizontal bar chart', - tags: ['chart'], + displayName: 'Bar horizontal', + type: 'chart', help: 'A customizable horizontal bar chart', - image: header, + icon: 'visBarHorizontal', expression: `filters | demodata | pointseries x="size(cost)" y="project" color="project" diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/horizontal_progress_bar/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/horizontal_progress_bar/header.png deleted file mode 100644 index f28ad4a3ce4be92574a3fb2806565e92d5360dd4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12740 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 ({ name: 'horizontalProgressBar', - displayName: 'Horizontal progress bar', - tags: ['chart', 'proportion'], + displayName: 'Horizontal bar', + type: 'progress', help: 'Displays progress as a portion of a horizontal bar', width: 400, height: 30, - image: header, expression: `filters | demodata | math "mean(percent_uptime)" diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/horizontal_progress_pill/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/horizontal_progress_pill/header.png deleted file mode 100644 index 2eaeb2e976a7899fc57b9b1ba8c890b9163cf73e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19006 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 diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/horizontal_progress_pill/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/horizontal_progress_pill/index.ts index 9b3aea2e55324..d5eba32325d1a 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/horizontal_progress_pill/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/horizontal_progress_pill/index.ts @@ -6,16 +6,14 @@ import { openSans } from '../../../common/lib/fonts'; import { ElementFactory } from '../../../types'; -import header from './header.png'; export const horizontalProgressPill: ElementFactory = () => ({ name: 'horizontalProgressPill', - displayName: 'Horizontal progress pill', - tags: ['chart', 'proportion'], + displayName: 'Horizontal pill', + type: 'progress', help: 'Displays progress as a portion of a horizontal pill', width: 400, height: 30, - image: header, expression: `filters | demodata | math "mean(percent_uptime)" diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/image/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/image/header.png deleted file mode 100644 index 7f29fc64c36b9a5eeb80188e8d1858e5898b0b23..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93226 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 diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/image/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/image/index.ts index eec1e2af61aad..ed7f6a99ddc32 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/image/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/image/index.ts @@ -5,14 +5,13 @@ */ import { ElementFactory } from '../../../types'; -import header from './header.png'; export const image: ElementFactory = () => ({ name: 'image', displayName: 'Image', - tags: ['graphic'], + type: 'image', help: 'A static image', - image: header, + icon: 'image', expression: `image dataurl=null mode="contain" | render`, }); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/index.ts index 6c0cd7eb33dc0..ec3b8a7798be1 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/index.ts @@ -8,8 +8,8 @@ import { applyElementStrings } from '../../i18n/elements'; import { areaChart } from './area_chart'; import { bubbleChart } from './bubble_chart'; import { debug } from './debug'; -import { donut } from './donut'; import { dropdownFilter } from './dropdown_filter'; +import { filterDebug } from './filter_debug'; import { horizontalBarChart } from './horizontal_bar_chart'; import { horizontalProgressBar } from './horizontal_progress_bar'; import { horizontalProgressPill } from './horizontal_progress_pill'; @@ -26,7 +26,6 @@ import { repeatImage } from './repeat_image'; import { revealImage } from './reveal_image'; import { shape } from './shape'; import { table } from './table'; -import { tiltedPie } from './tilted_pie'; import { timeFilter } from './time_filter'; import { verticalBarChart } from './vert_bar_chart'; import { verticalProgressBar } from './vertical_progress_bar'; @@ -39,8 +38,8 @@ const elementSpecs = [ areaChart, bubbleChart, debug, - donut, dropdownFilter, + filterDebug, image, horizontalBarChart, horizontalProgressBar, @@ -56,7 +55,6 @@ const elementSpecs = [ revealImage, shape, table, - tiltedPie, timeFilter, verticalBarChart, verticalProgressBar, diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/line_chart/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/line_chart/header.png deleted file mode 100644 index eea133ee3680b67ea5295f8cf112b3203f89b313..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59575 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�Gf}b%;Dp*OVIQ=eg?B^*_V_Q31sE de^mUtp*|V)uEnCw7G%IrN5eq9P}Mr(e*hV&yhH#1 diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/line_chart/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/line_chart/index.ts index 5b8533eea65bc..d19ddeb00dd67 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/line_chart/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/line_chart/index.ts @@ -5,14 +5,13 @@ */ import { ElementFactory } from '../../../types'; -import header from './header.png'; export const lineChart: ElementFactory = () => ({ name: 'lineChart', - displayName: 'Line chart', - tags: ['chart'], + displayName: 'Line', + type: 'chart', help: 'A customizable line chart', - image: header, + icon: 'visLine', expression: `filters | demodata | pointseries x="time" y="mean(price)" diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/markdown/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/markdown/header.png deleted file mode 100644 index a8b8550f5baeacb507a7f24894a481f5d8782767..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33733 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
?~uZx0w-4q2)c8|;-gh;`i3 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 W_CYSK;^Dty#me&Paush(gZ>{VnI@V5 diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/markdown/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/markdown/index.ts index 1c7013834cbe4..7b114daa11870 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/markdown/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/markdown/index.ts @@ -4,15 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import header from './header.png'; - import { ElementFactory } from '../../../types'; export const markdown: ElementFactory = () => ({ name: 'markdown', - displayName: 'Markdown', - tags: ['text'], - help: 'Markup from Markdown', - image: header, + displayName: 'Text', + type: 'text', + help: 'Add text using Markdown', + icon: 'visText', expression: `filters | demodata | markdown "### Welcome to the Markdown element diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/metric/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/metric/header.png deleted file mode 100644 index 0510342cdc54affd52f42a8800c806d2c58ee36c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12631 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 Fe*l71P5=M^ diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/metric/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/metric/index.ts index 7e9aca9c01782..7256657903aab 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/metric/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/metric/index.ts @@ -5,8 +5,6 @@ */ import { openSans } from '../../../common/lib/fonts'; -import header from './header.png'; - import { ElementFactory } from '../../../types'; import { SetupInitializer } from '../../plugin'; @@ -14,11 +12,11 @@ export const metricElementInitializer: SetupInitializer = (core, return () => ({ name: 'metric', displayName: 'Metric', - tags: ['text'], + type: 'chart', help: 'A number with a label', width: 200, height: 100, - image: header, + icon: 'visMetric', expression: `filters | demodata | math "unique(country)" diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/pie/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/pie/header.png deleted file mode 100644 index deecd1067427c04c9c505b72bea6d9e20f545bbf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12580 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 Z3lI;ieC2v=|62 ({ name: 'pie', - displayName: 'Pie chart', - tags: ['chart', 'proportion'], + displayName: 'Pie', + type: 'chart', width: 300, height: 300, help: 'A simple pie chart', - image: header, + icon: 'visPie', expression: `filters | demodata | pointseries color="state" size="max(price)" diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/plot/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/plot/header.png deleted file mode 100644 index d48c789ae5a92830824fed15448602ca1dc52be2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37595 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 diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/plot/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/plot/index.ts index dd1660d558667..8648b65def4b2 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/plot/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/plot/index.ts @@ -5,14 +5,12 @@ */ import { ElementFactory } from '../../../types'; -import header from './header.png'; export const plot: ElementFactory = () => ({ name: 'plot', displayName: 'Coordinate plot', - tags: ['chart'], + type: 'chart', help: 'Mixed line, bar or dot charts', - image: header, expression: `filters | demodata | pointseries x="time" y="sum(price)" color="state" diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/progress_gauge/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/progress_gauge/header.png deleted file mode 100644 index 8340c8a53b6ce2e78a2ff7ec334eefebe7f95f78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22905 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

RLKJyw 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 diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/progress_gauge/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/progress_gauge/index.ts index 4ec192fb787fe..b21b7df286ace 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/progress_gauge/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/progress_gauge/index.ts @@ -6,16 +6,15 @@ import { openSans } from '../../../common/lib/fonts'; import { ElementFactory } from '../../../types'; -import header from './header.png'; export const progressGauge: ElementFactory = () => ({ name: 'progressGauge', - displayName: 'Progress gauge', - tags: ['chart', 'proportion'], + displayName: 'Gauge', + type: 'progress', help: 'Displays progress as a portion of a gauge', width: 200, height: 200, - image: header, + icon: 'visGoal', expression: `filters | demodata | math "mean(percent_uptime)" diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/progress_semicircle/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/progress_semicircle/header.png deleted file mode 100644 index b5b708529edd44803d40ec56de9080ef787aacc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16999 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 H7zY1e=XL3J diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/progress_semicircle/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/progress_semicircle/index.ts index 91fcb24996bc0..9ccb9489e8306 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/progress_semicircle/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/progress_semicircle/index.ts @@ -6,16 +6,14 @@ import { openSans } from '../../../common/lib/fonts'; import { ElementFactory } from '../../../types'; -import header from './header.png'; export const progressSemicircle: ElementFactory = () => ({ name: 'progressSemicircle', - displayName: 'Progress semicircle', - tags: ['chart', 'proportion'], + displayName: 'Semicircle', + type: 'progress', help: 'Displays progress as a portion of a semicircle', width: 200, height: 100, - image: header, expression: `filters | demodata | math "mean(percent_uptime)" diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/progress_wheel/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/progress_wheel/header.png deleted file mode 100644 index 71e5d7e29444e961729d39b5668586e2f142b079..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26408 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 E4@#CL#{d8T diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/progress_wheel/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/progress_wheel/index.ts index 05c537f88756b..42bf36346c303 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/progress_wheel/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/progress_wheel/index.ts @@ -6,16 +6,14 @@ import { openSans } from '../../../common/lib/fonts'; import { ElementFactory } from '../../../types'; -import header from './header.png'; export const progressWheel: ElementFactory = () => ({ name: 'progressWheel', - displayName: 'Progress wheel', - tags: ['chart', 'proportion'], + displayName: 'Wheel', + type: 'progress', help: 'Displays progress as a portion of a wheel', width: 200, height: 200, - image: header, expression: `filters | demodata | math "mean(percent_uptime)" diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/repeat_image/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/repeat_image/header.png deleted file mode 100644 index 9843c9a6d02c04b4a8421176811549a84209a3d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 83775 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 diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/repeat_image/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/repeat_image/index.ts index df79651620642..0bf0ec4c2dc1f 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/repeat_image/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/repeat_image/index.ts @@ -5,14 +5,12 @@ */ import { ElementFactory } from '../../../types'; -import header from './header.png'; export const repeatImage: ElementFactory = () => ({ name: 'repeatImage', displayName: 'Image repeat', - tags: ['graphic', 'proportion'], + type: 'image', help: 'Repeats an image N times', - image: header, expression: `filters | demodata | math "mean(cost)" diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/reveal_image/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/reveal_image/header.png deleted file mode 100644 index 8dc33b5a7259ed773bc00358e9ace272e72b3450..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8989 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 ({ name: 'revealImage', displayName: 'Image reveal', - tags: ['graphic', 'proportion'], + type: 'image', help: 'Reveals a percentage of an image', - image: header, expression: `filters | demodata | math "mean(percent_uptime)" diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/shape/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/shape/header.png deleted file mode 100644 index 3212d47591c07166b57e9e3b3ea1dc7eb3a190b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10388 zcmc(E1y`KSvhECS!Ck`O65Jhv2ZB2U_u%d@xCLi$cb5c*;K3!hySs%DByfgr?|b&X z>;8Z{tJkcSlCJ9RdaL`1R92LEgF=h~007>6kd;&g0D$q(dL9x2^q=ypz#IU8^21s} zLivM)1W4K0!NS_s8~~7wgrp#MgKE;#OIa2HuOBZZrB8s$qrQ$P)O+r8@ZDqmC2RP}+2vqj zbvWCF1yFRyl+Q+{2?vq@VM+b*n%>#QIc%W=z(fHsn1GNU_^86Yy+MG2^!tkkM}jM9 z*A)|v`L35|!{Yv}s3m}yF%q3q|0!CC66Fq1k3JCqUJhHlP5ls(^kosK5kiW&RhcxF zx&Qn@F=KyP74Xvva7m3X-wz88F+)0BvVDskZWdYx0rwGrvLfo<35Nc7?@8Y4 z!pXrw*NS7DRMgeK^_STqk%>0?N79umi(0SG&5vH!`KXs0f#+t9>0(ZwL#p6p%Ii2W zi^5?a`BAjwdJqG(YuJ4j!wZ9A!h_5to8Bpet&cRtBhS{(TlOa!-Hl)RS3YcuS+r;l zDzmFrliVR9i%5&R#1bi=q2GHP=Ik!21tyk!ndCSeecKH~-8$r~>H5?$@!9-3yYEKX zC7x0{{(MaDqNW)!Orf&*ZTrb7>lBT&ra5kmB_ib>i6-akpWa_=>7u-AN)`l`G9&YH z@g|x2B(&|h>Y8GUXu` z7V-XrV%fvDL($vQ=`c-upW0#|+_2!`JO00SU4;tY1tZ-3=2PYnz@{Q5yW`8+ve{Yw zD1g1bDuowjRe_-uftB%;sIR4Vhr<|XAQ*Tt_+v4Wa#kE8peDV)dSKxupA~{-H@qYKDqt;-KnM=jh_C~$a1)j= z9E60JK!H+(ek2wa&u<3H4$>MzL`67k&Rwx;< zO=5zDf2XFbcmzQu+qtl}G_;_Binzw41 z$T9q7S4vkhF|jG}^9Q@4$083ij-`lGMMaSOklwJ{ZuIWpuF^HGm%LYrkc@w|Z>Vq5 z6Z8E73LLxyJVq!D3O`CQ3LUOJV}~j=Ju!nf!&lq|dQJKq%~Lf}wE}vbccF~t^mvS9 znr$V&J`t7L7M&K3sZ40B*ZQh>MFbTbs&&xYj!2IHN6bgq@Z`BmILgasVUATk6S%2M z>6mxQeHn3vpg_Jc!gAq`M+_7d&qP@bT64h^7Vg&!XpCyv zsGn#&7AR`nsVJ#!)T~!M%feP0mtfV@e>ANfFptr4DRap>s5{u5bZm6lOnXsWq2SLZa&bG-{c+$wAx8v+a zVBfF#>o>V(0i)ZWx=RAx-ob8_w|XR;BuxsT<79eYt8346SMXPOZ^jQ?4lc3-CSy$q zY@3&A*3L7(2Y(;x?D6mK>86;h9 zfBe4g<_3NMwFMQOiO_k<#kf_y9j#?rA4mVL6}{!Ct$y|MYEH{ZyUQv@o9GSOt?Vt| z_SN#th}y2#$W38t(!%Fj9*5-ST#rtV)VQxo4N9$(riQ|XIS$`1%5t+er>o3i zA=^$}Rh?mP{g&|--j)(o6_uuxtE4Jrs`T5~C(Jz}4cuF{WoljGwYPMGhqXqOnY6 zWHM(KPKfyMML{U*4+L+d!Z?P8J&7?%ddc8a|1_1Rmv#d|?}+3&_QZFR1g z26+!kA%~Dt$XTVUR<$8_%d)B4EwPotbjI(-2OF0K^x7Le4=Z6OzvTx*UTr)2^-F)# z(}DR<)vIo;5}8jta|go*k=GAAf(|tgz`w9B;Wf-ndhQPQIg`U(21Xjnx|fad`ksGf zPRK|7vlcTt8Y_o7tuA;@d3u`KSJs-y88sOv7~U>pbrhC|7$h2A`yWhaFUTFFYMxP^ zC!Q12qcYko+12^1oY(F#?0v_}!Ha=pK=c?z&$mun8yEZ>Bp{j^@8?qep8W1A{#Imu z{_ z;=IZ2tm~}G?2EYZNnroML9EeA%E7L;#$s{7W#?3 zG3_F->FQJIa#C){6i_Z)=u@&?HL_IT((lrGKs#}eMQ(%B^u4XSoo%+W;qTUumS_)_ zP{ZRkQqTKiyp4ARfut|LbsJ`wWV#R?HTq0tF}k( zr}k$$KV8mKR;p2(zdIPV_I0=v@)k88*#BDd@r!;yICnZ1?#lIl84o^1+`(A5HGQ}t zXAoiZtGaL5=bhOgMm$BFK=;OQ7m4(}c8n1Ib?tF=VHoa`o0yv-GUKIrx4M6yjP4AR-?S z-49TGaXsYRaT!cqWStyH_50z+{Alq|ee1J%_Z;~p5}882KjSmJ@(?&F3FxVZ3gCZx zMAilbv1Fa+mcl9AJ*mMx<0#!Ns(nSwnCE^&7S+kJ8rW8SmS;OY zzBNF6mU;m&!N0kpTX~lV&2Y1=)wNx<6+Q}>I@qxon>m=6vv}G$LK9v9K*&=7TD3EG zH3oUw+1k4ZcnVYgLm>dIzjm`yg8m_KwGpP&R!{~>I5?YwxLLSZ*eFF%Kp>Eivzdi} zs-*P4*`Y0AN-I}aM*&t=4-XF(4^9>bXG>Ogetv#dHV#$}4rV9?vx}F#tFb4uy$jX9 zg!~^llIAX^&eo2u)(-ZdSGmR}4sNc(l$5WE{`dEqSsFF;GkZ0Ftc_lJC_$foIvsuAk<-2D^x?@nfVj zi|h9mJ$t}FQ&SSt6wE=RaApf+cH_t%F@{}gbAIcg{32_s83{hH3t%RviB4fz0>j#S zIs}uBAz3xgvxg@QfZ!lb=|M=G^b@``ydLRC;>z>fW93 zc)7@(~LRPU{WJ{8tOwkMRGa zzakI-cMAG>b1W|S)y+KT7S+(r{JS#4U*tUag047a@mC{$CIa9j<(u=AK>!#~o+uD0 z;#ou-`90JMF?kqFOy;#9@j$3~;Ag0H506MRaIdB_Ba4YOxT2!K!$7$naKPZh3#w#H zII(V8m~caFYqg1qA$*u^G|~KI=y(OP8AAD8u?PKkzpmpfvLtf2e)byi_fQq;*s{+~ zf^P=YlC>M9%$s6IP+_*|J@V^bnUMOm?0LS*RfU0ZP0)c1k_dxPh7Nf#S3??22kzrZ zeQnrB?~c&|*>MVx)aE8Gbc(#l)S|2J)7Sg2Mg}*QU7v*4vp@hKe-F&XtMj`-Y`&{h zE5yK@e5ffBUYj^jmIY)q>>mrHXPqMjvUW=n=`~bKah}4`fYH_iArzmTzYV3V3faSG zWU{~1JU)ZHAEgWryFtaAL?aP4I!>_uqM?<(xYfT^BTut8Bt!XzSuPLLC+ANAwPo;?%NkTSWqoEVQssny!Ei1&q2;xa~QSYq8F zPV~=`^P;P}c2jG`8r11A6$*ha8gVs}ApE@$GpONam=RrWt`Fyz4M`+%=v9w}TU(_< zk4v{OU%%DnuAlOWzq|Xb5`I|lwNJb1&&9$DhFay}_QO*e(zs1^@fFs>PBSd;H5_mT zPBk<7i(5oJmVKyXC)b+mgMIr{K|9it$cVxMz0AHNcsi^1~`?Cdi_|dhK zO2&$6p~RFj0_LSqU_uZA1xSJ%YXN!aq@c0hM5eZZr6T_i<75B!K)~vc#=~U#ae`{> zSF8QtzD^y4%Ejxi_iF{$SXt~619ci%6}y(#caXLSpBiF-Zz`n}scxpb-f zh_*P9hG^sMR|HM^?~PsnH1mxr;~n432nMl^1P#QN`%^C_MUUodDo4KD>By$|wd5M$ z=<79SEsLp1j=~{8w|4OjXP8~V))knMy=xNm&U!mXXJVZsY;M2F3Z zZBA+U_UsyECoh?d4#eu`p4NGHF0x(oj+JmB>16aa40^(bDB}5=Tz$&R2N#SYJuLPJ zfmmvFM@w<&KfCM&)hpuk-N+U)PbNu?poe1~d($b#nW^4_BtaV*lJ=2+Tig|Ib-Gg5 z4-an|x>}yc0=>GSOJ7Ctv#UM&LG+!&itc)OeCaRmwFUxAgUaJt9#>);O|LbbPXU+R zYOewb8GaWk+dG}Amw6D(Kbq9{$?%>Q{xvcGw1^Bd7Tn544qX;8b2#jG<1vL(apHUU#iL$N~ z_3lbSK%jX0Y#DjTy`FV2iTsqVJ`Se}4rVOU zi=FpXw-+2E9#~wyD^&~Sz@QoeCRMaN93;WO?CO=H1K1aT>xF>gX}a}hOPbxyL^Hg( zrx!JKh)X!;cWJBPc8RbK>TZzyKV*9Gq>=&v9x?Xmx42Tnj3u@9LKo#4hYewp zIjcW`N`LB7nAdzI?nr($d3g*cL0EpuRn4+7hh;vMp^vjKkKJ zZsv9pwQf=ikH<(5k3RL_X|7c}5SR8CHPDeRhz8f8*nK*ER_rYAXN~AQTA7&$R9shRy!*C^9^0_TJ@&%vEZ{22I`y_Lm^2YL12J&cV1 zBg++Kh+;$F#M?bnY$n~cmrF(s?gf42)mQ3}gng^W_tSixR5iOIMWApFD@(wA=qf=()*;aLLP4!Vlv9=8 z(SQQ@8}Shy0mcH!kwE6ORggjGK%KLB=j}LB9l7b~MV|AI;7SU8G0eyOr5WcczRTzw ze`lJBPe5AA&^IvLdQJ1}joE#ou~;y!gcazhuNqMwr)ba>=fwB}*6{c-G;% z7LhijbQTPRlf)0clm^FjN&am|Z!5DnuaUcLQQ`446w>xN(BOCe(Hhpph=)D zLq!WjvpBjS-V~fop!4r&$aYU?G9#$tMbZQSV{$)Z^gig6;1t6IC`;{x4a~v*-2Y|O zlOh3wor(^q>Lqqr+kQmZM}A6BsqmRVQ%UZ6pILL}Qrr79H-or54m=o9&^CzdI5w0h zPtWDUVImzXq4F3O4CF7!D%Bt5IAX@sftP86;E16XIfu#S^8x$6;2uz&RAH@9n7Elm zA*SkAfT`<4E2<9q5?!~*<=H3`u#iaOi0)@bhX~4uw?1dpgaB^rS}c2}7SXXPsaA@9 z8~(yD!LVOKucG$*Q;O}~())^Uebnk~ZE7yEu=8qP$%o z+AuOXG}s9#f!O$^>n&4N&3gEAERAWvL#ZWt_$w-ktwl{@73{Euq=xie_|Gb_q(mq% zoyuV1i>_%xBhiMZE-SV`BhzmfVP6(woku7&#KbkydbMIX)eTW!%4lPsoRMcEgr$Ql z8;D2qidKH_=kz{eBRSe4PJT^f7j6zYjgc>MH=Yv{le-OOUg!~RGLk9bhpUk0RX!cA zRr^)xA~i#O0Jn}$3-Wd^;57g-Hx)V#(B&p9!<1u~4Ez`#y5)y?Iov^2N-Y{ux^12; z{8{y3_I4I!g!K4^E7k;Z$17cSBp6N4sU7_8QWb-A#hd{Y=*{#EtBrs5dKSCF-fp%vZF+Rz z))KvOVJsylH65)8b%+lsf~ZTj3(-FzmXJtY7DU3S>Ts;HX%jo&@_gk@8n`sFph_(2SwMzLClTm32MUSS^zQ$N|E*p^knJhQn zqmA>Vlb|-vOSAh;0;RUKddNQOOt-b^7jxz5v+UUFL0Nc=k3^=n+@mv@*+$dtb+pp3 z>T1lwBy@H2C0|Z4exstnWl44uVraXQNY&=Ya#Z#vQae_d54^qdsm*MIivY)Vk z&wJ;Usa*%5W5<8`bI6{|f>~J|%ZFnNmvT=ajvg z(^K+wUlcTlaW?)~i|HGFKm2vO7b7KKQk(}TqITKD1-WWiq~7@(H}X<^6bF#MkRqZ6 zhlDgMlg2~8p4&~Dle{rDgyWo+o4po9xHVsBokPv)=o zAybEV@^wBQYZte?jpSYl)0)D{g?JP4DF*Iln(qD5OupUwijb~lFXqKkv z3S3AjWZj#wa}`BcR+H4SGmXa%{zFkdeBgUZIvKmv-uPgu?AaLeOfcIRYgBh{w2Wy? zmFn9n=UU^MF#-e_dy4o6D>`#;9Q$&`b?-XQOJYMsc0KqIiZw59syTRD)-bgv4ORYh zI+xHua3&I8wa*}v58XJ&x4)S_>Sa;qB89_DHmj-OfE%+KHcUqxAf2KMcb_Q5_WP{q z81r^8+ot@0EUNaFh8}zv- zYD958r*O^>OiPO`3SwlfecZ8ul_T3r8T%qF{9d};CSCnm?kCxp}{n)J)Z3|B@ zv=tOQe&ki|`;Iz9ppK&k(QY5ds(fQZ0}}f4cxF9LsLpyr0$3e^3mYYq0^6LfPvi9@5%M5fl++&B6rTV#zm63AT#1Tn^*3GvjU00ORqg~o$lKY!s@3{+gu z2*>?9wQorfGiiJ}uN(?;q5plVit;fQPfA-jKh#8NL$Oh7oXI;Ik8UTb{cJ2*M-aFr z_t6Gz%5W?~1%kn-I#xaw&d<)8b_*AjIrAV6xm_Fm-w*}x9c74OW9zb(1Ui=qn z$G$4efr+cFvBgI|g=XG%u9HMw9%vjsh6~#pz|Sd7Q6G+UY6deFde26e)(BT|T%!DI zJ6FYXM0E!u+IRY|OOyv`e4>}p2qE$JC-jO|vx@RxUPi*S2#dj%ahz^1vb;oV+9tV} z*$R3Np}gPnke~aS4e9$WvN2awpXqf$JM)bQ6)9z1G44HyF%?7ItTFaW0M35MPvgw z-&u6%W;IYH!D8^+S61Z8O-+g_^?YdF9S6tVcy*$e#DeEkmYxTYxL+=9)B_%cPC758 zw48qppfX_{Y{5Wv26am!QlJwU0v|* zV#tuQ$*R{kr*`MW_!{|gtnwG#Bh%++*n21UBAw*4abS%#-p*sj ziY}Yy_(gP`Vi6JgmrafSi)yyH_sM(7OHRS49qNc4J5R1B6rTmN7)xtnJKW{;EP5Jx6X!W_~R+sGen6(l_o%$=Ui(xC*q?vjKBL)yti1WoG&oV)%rc zr+vdV$Xi{B;U|e@^p&1crtN&fb6HMYJ~>*AefmJ%)ctdubOvS^e$cQ)DN8gAU#VhJ z__XcrlV}ZHf1kjDCil3|@^P7%kh6RrrCEctWE zR?)Q+jVs{5;U?S|#1a1Rk!EsDd+SD#+pTsi(>J)+%p#BROyhC;SLK=T?D?jB^{_ab zeVR5a&yqkJLL8&J`|b?%dC+`GvpGv?s~jWZ{k7(im&G<&f&2;4@kW=Tx4{zHF+pdB z-$}*-?8YNJjp(Y1yB}Yo?=01VmXL7s=@q$-sIoGja^`+9P3BHQ${ixoek9@MTRf znS6hXD!-^5f74DIzdBvX!2@-_srJzj)w}iLoB*$2gqb~s$vybDUbN|o`-d!5zMy;g zw3qqwxS4nD-i_zW+cg!VgHqP^(T{qh_r5eY+8}rK(1|}KiJCH<-gQ6&q+O0y>F74u-=-&Is_)23HF z86aN?^&hHijpct{`&6w72s~8AN%0D9^eDL+fv`KLI7Bemgu-#+r5mPtl~i{+B+og6 z(usO6k<*Xsr2ZA#llIt{IZh1+^Lvu%+X|THB^7$kk!f38%>_h?czw;F!2S$7*TZyC z(klPIzxLq1Xj3pt%! zZnT`PAc~AsaWn5n#Lzl4Re>am=hTDlbg-eXapat1p$TU!0tNcGiBq5Ukdi4Ckcl%9 z!#2lM6InLzmIFnhRen@cCg(Fr_?LOv6(xqXE8Tqg5rK1BApf5iW+v6LY^iL&1xq-(=Pnu^g2Xu60|E(n0?miy!2ytk zoR_SfL4j+{nxXfs^Mak;(e)BWf9(v}+|gW3jEt~SHGQLY488~}_}48WWqhf{mtV8_ z83PY2Uu=}^{tWZ1JjPiaD}L&%M>Wc{>sr(Ee#Zp{?v{Q@bn?rGtTHZI93!Gp!+9B^RlO zH?jl5Z+Er1v@qK2^bg>InWC674HOFNC^B7g?$IdbpcI* z%Vs^HG$5R0I9<<@f9Ju znL`TzUNtgVtq_7juYeCyijp ({ name: 'shape', displayName: 'Shape', - tags: ['graphic'], + type: 'shape', help: 'A customizable shape', width: 200, height: 200, - image: header, + icon: 'node', expression: 'shape "square" fill="#4cbce4" border="rgba(255,255,255,0)" borderWidth=0 maintainAspect=false | render', }); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/table/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/table/header.png deleted file mode 100644 index a883faa693c1f4e14b2a4e70494717f83d0f9ceb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16125 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 Mipq&p3F!s=e@GTa3IG5A diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/table/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/table/index.ts index ac13b7dc21293..ec26773fc3bf9 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/table/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/table/index.ts @@ -5,14 +5,13 @@ */ import { ElementFactory } from '../../../types'; -import header from './header.png'; export const table: ElementFactory = () => ({ name: 'table', displayName: 'Data table', - tags: ['text'], + type: 'chart', help: 'A scrollable grid for displaying data in a tabular format', - image: header, + icon: 'visTable', expression: `filters | demodata | table diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/tilted_pie/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/tilted_pie/header.png deleted file mode 100644 index b3329f991158c0ca52172c8b8a00e6103430d6f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33235 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 z 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 z zeSbbJi@fGO0!O)N`x>)uK?RUVDHVkdfWbwuy=i=*_pmm@{c8wa=!ieIYQik$X$^^d zm-BA>abWUYyhVaZ} zlT;r-xUY`AwABc?GvHAMK9Hw$c*@X=xGdW`E_NU*my&Ana9%45>u1ns ze@+c(buwoD!B&(-)8rj zdBtm1?4g%7^V~%hNoX1X?MB{e1(Q0xy*~3(_;!AZ8Ha-rm6iBtU}Hh(EB3^S($mL$ z4VUaE7Bw(&rnS>u5bQhtwp&Pa$?)672qv$y74H{iiXDPEZ|8_1hVO*kwE700Ka?0j zig=Rw7XqceA(m@V5<{-khG8kNeBO4-pvlfh zw)kZe3?dxo_S|lVHdH_sM}oO)kU-ikv?_8RiVZG7JU61q&h?rSBH|a0AWcTt8M7gD z0_)&!yu+N$75OH=4|-OSKY6}fmWzxS>xtXZw-sFa+&2^L|Fo?CeP{lxzmavoE^S7o z+1B+vHa|`6%eM#TC2{x*0*W{(MfBfo_STCsoQ_(E5sxSQ>;X6W%Mq-d-%v2Wu6f53 zt9b%wI~VZLT{e>)vci>ANZq+^0;g!OX@+keTTpt%O*=2#<%Zhx5O3`G1NE4F4>kk>X zxfm1wsgAXfoo=w#$7Ipb43gWyBab)lXDJ=-osk0p+n{B%w1Q*W`eji^QPK-j%s({J zqu+U%_0P_OBSve=cJb}?LJ&-c+pYAt5uVXC?dMX~a#uM>>s|anMcg9?!?Ux7=ZO&4 zo;>E5{N@zC0TDN*9HP&LcEXAm%@qNE zjA;&?6KnWvV4qgXK6a@arPhCo`m500-#(3p3efBn6TerfksdEAP-}|F+cDEV@38rp zqL%9GF)zC;yhVK3IE5}vbKIz>9Y|fzuQ{I}=VW5XH^(DSh4=R7VJgDCQ-6@pj5?x1 z3o_))Ozya4p8xi53NAxUcP!&uD-O-D8uK#L%{?VGzmJ%ZfE1y)7HT~dY}nu{%Hztx zjC1Rot<~n=JcE7}N4ur!)1?hP`qqIdy2?R-C3z<9{OQ%IHJ+HQ1R z>YTQ?bC!`}1fc!Wpo!fPf}d()Ekhvd8VmAh^&SyJ(4ukX^sS{TO5~*%V@3~?0(07n z?ZRO=AtR7j)yVt{x5ot@*r@1cRNhTX{^~wgMu$%MDQ~`7+d2xC>PJQA((IUt?``<0 z19+YeH2y_>`SfoeJ04+TN3M^{v8D*=8RxOaII%|Nm?0SUXC}t+zk=8-?5V`~lX!fK{9(CWor;!v83fkFS zA75;R-Uy(9w9=4$+pp8M+wt^;0vm1ccNy`6gY78!mQK3zImdn|A#WnQCs`s9fKNE- zgla7e`qNO`?u7Z0uNpDWcO<_6y%=>Xb&5(i&T?`6lp;0%bg{2Js|e30XMPmv<)h-B zy0i}3E`%*UZHh~iPbtt7thcWxSfR>sz&OvgD3I2KX;kD9oQ~4EDHPrc z0Jkk%Kc$wGF?`Zk#S$ksFos}?)kXf$Bo^1{%c8CFleaaP?r3~OT`QG|VsW5>Kfa>q z#cR=+sXTu!67g=fH+)$3u*)3)luewGa?2X1$`Q7wb9&An$589~QAQdSn4lYoROrAZ z8pG}Om{5l`ug5EhK2Cbne{B*xl6%cuCKvG`9xHX%WxK|fdV?$H(QI_n4RzSUQMj4v zY+&mi19KtxSdmC%vuk)|FHB;j0J49R^R~(21AF4;mc*Kz67e9w0L8Yni7kB792)Da(x@^{v+a+<6*^wh|UphND|O`+$GeU5I|k>*#7#fNSfC;A1=v`u>YLZV0| z za_8zpz{xYJAbtj9GZ~^mo~3LB+!rq#br7759VHZ^V6SmHF%#r zKI*OZAKj4;r4r%!Nn2Bt9vZIBKW(YAm2qK%S(t+|fi{QVl0&&&G+9iDGyWd5T;(vY zMqAg+41}aTIiRb~vC!0oFY1Xq$42ax^)%i*=j=R9~(Q|x?bQxQ35R+h( zrXnt^`W*`m45mq52yDjthUk2PErJFoU}wh}2cWq$Tg;iMpGK(x%6`RXeZOuDI8j$= zu|U;ES}Ri6bThan=E6l5OoZi6<3-_vhi)Rw+Hw;EBh)b zD0Q33(0zMJC~6dm_Q{OpysuZ^mwZ(bngiIKzPCtG(SRjC>m1JsunER@uw}}RfW3la z$&}8wbH;!3($KJ2^a(vmWeEoB@OM zF(P>3O{;@F2otFe(Z{bX${3RuzVw(T^g7Lt-FJsEa4y%0E{gKZ0G_M4J!6T414AFo zzDr-HEE-J?M^m3YANmW+rm7%4A7)<;>cT&7DoG8m&c(N`P8uGij^qyAk07|}({b38 z4@r9|ximXvA=fP=_cwfVyf17h{u>0a7c{e#jZ;MqRCArz)K81Xb>-KT#QqR0vnBq; zkzaw1mZ5jQWmFu3nEX{w!?C5B#J3!!yn}}1rUcE!Fpm@CeNbAiovv$KABoo;U(LnQ z5X)#t?n>ObwQh5r7(OdtQK~NuL^AIMn{p{@2?I}<(U>c)>`EegfV!!ywx9f3Bej`3 zxoajDM8?JmUo!+NnEruQTtVGlz!TrvyFeg#xQqpVxWk$7@ymAXFqKY4tIqwoHi%5t zd8#_QCxlo)IOZ@lg^m}VrI!#D;0XB)mKImrnfLL7_UPfZ7ce+&w!>plRYGN)RxS*g z;D^D7maDZH&z;-zDqb}?T1P$CNz9wu@>3-ua}5l4j2F-Si7u@HHVL504ZDWQl%I2HN(icC;4exa##g~XQMyg^M`W!0W&PaH zhHCYNm2Y@@+YrXw-wDWA)NrVd+F<{YTcfsu9wsm(F~X};(mjZa8ajt1B7c-7g?7pK z96Rnu3FrE*Fj3PYW+ku`D zCZ;?)Wiy@~^)FxJY5}FirPVA|H_3VyRGIlBPQ)!S>;~Y9PSUIr(7w&BuET)M=2Mf` zr}P73(Ac?y;N#Jz;)~8rj&dc8Z76fDUilJIIf44Nc~?tq%#|Qc^`FstMtpev&@yNH z$dP0~kf;zP*$Mo)kiaDGrvMDYn%~RsxY*y*s)03-|ZB)alr*f@a~Z7!qv61+QVrbhanhPhRbOK=XbERxI$3=qE> z8}8xh5XPqR%Fm~H%7@lZ~3iT2I5@Ll8!N*)|50TyU8kvj|E$% z!HmfvqR>=#3!H~kY$}xCvYQ2soWMc5qSV-aI3khMw`y1zZ>@s~Z%krk)-l;d!1F+< zlp8|`^my&`x?x6DNDA$vgRvIQ4Dj$YsTnp+o#Kkz5-vFmuMyc5MlHDbp$dNc@Ep{J(wk&*laX6LFt1TZBdKSg1feQ>_P5{b$ zfe)omNyGupY7;vh#MntWl@D5HqY+8y3YhG>SpOEbu(sJJXq-Z;RAc0n>Ncx0$;bUV zTO+H57$$*?k@|+5iYCxAhw$b6;@;PwgV$#`ZQDQIA*WTy-P8XRzZ1ZF(~kfAY{52X zz(mRzEG;2i_~eCAb+*sMqjocJ4VQ{43S3}87#J2S&8Zm@g*^^@1NeQsPVW>$g+XR$ zFy@wS*rx&)twYjRqE^jni#inC#RUj4B)IS;E!#CX3k;POUAU3RrDIe{GzAx8ZE?+g zrnNR}j9P5R<-SPl5Hk0^SXgN?Ybl;JcO~e-9{TgwHKFBQUZ+0X#pJTuFWS!c({?BK z)+FN5FOLcM4ZL+Qi(h?jyCpBBnqNFa911xA5Hh$9;6moeaWf?I&8`0qM8Q|>zama5WnGilsnFF65` z8tPzBph=5ErTws*sKjyiX1z+~`RA1S6M^zs9|2XiKx^8X?OiTM5O|f@`>wL;#C$?3 z{?o}KrGjna8{|gI2L92<-NV65Yt#D26C zMnXMmzJ?L{@KMfs(zUl{^-Gh>_;@OpuT!1HsxE)s!!KKd`oVVOS?@1XCFS{g+E~(f zFqM%Ccz13~r*Uv891o$f&g*$37hRBD-j(4^ho>s0$m!ZzW@g_*4GeTG95O+_wposQA5&d-5r+-`CR|3HKN#m|3DwS zj9I(ivNNW5wE#T)A$YU~o*D%4`ZtSR?+FZb2y<@Lt38+w%EQKc#B{y3jp44F2Ma?BUN|tViGbksS2bGz?(-bHwk2 zi##^iKv18*zcmz;?g?>>Ew(v0fFRUydd(bKS-sD7T@))?UXP}l}~F(wDOSEHDmvP3ee zW_{FQC8cN`DKkA?FCD;K)VLoI9yD7hq#$5lJKEF(pO!lsES&IZXUo(Ol+doOu)ukl zjr1kn#oDnYdBENphqHy`)%$Fv*ZW|jBBx3=ZzF`lj~FVmmOB7R4j%6ONbwrq96t18 z-<)EeL(i4uPmAkoO&b3$iI_CrYb7px_oo@=Q%JQY9?NFRLeyC>;_ z`z;8qR&$-O$O6Yc1yD466WR?~-i}R<($fJLG*KI8j=wmiF|B%utGHh8A7V#eO_wma z6Hh~;6QmbM6o+am!p$ZT5S&k^Y_i+0FGDdGP0Wpv*P>VOt1#+l#vXkNCu)nHKo=7^ z{mOVTItxLkHddG6ki(WyWIzyPeWNk>+6T2$T#Hc3`|Sq@F2Za5Goqkl2^*wm^^-wg z|E=DFcl&uojtJ!1SY$AiqL`rZPh!Y(;D->*c8E){^%J&LRQc3A_#sD&5q*VC8}VxF zJ9Eo31QWNz?QIH*o{r+#NwIUfP-$0@-L>16sC{{MLimI@B?cl5GLz`&eQgy+8b*{l zQa5WY-@+w``Q#yglxZQTUg#=Vz%Qqt5@9<0{r#<7!8^~?>rFZb^x4i;X(vDx|4s0{ z`ICpLT{l3TcooUdl2a2ts21;m@rXM1f3?l%{#wkwsT7p)E%TL zqx*=RXdd5ak2-;&@LVljy2}cKoxwjy`+5Dln0J2OG0AUG5(1P0C}vB#JvNZ{`m)=H zg3>xWykzp{%vy1Dd0OI6urs!2hqc3>T*qa@8a}=0=?;O1H~sQ=K^jU*Jc%bzJJWSJ5(gDvfM4THxWI_tNgmXnV^FNd4EL_4w@=D>KJ+*ZafrfM%K4S=+5QzF_h#&NRhBJ+Wwft^&+DidgwTq`Rx;}+q3?H|cv2DS_P&|{kSAt)2!&SK z{?pmvgVT5sdlXM1raW&-{Kd|K<>ssG{_LaMiPugnaQSzlZJfn_J=s;R3&LSWNf4z# zIRgfZPnzmDt5}%Yxz#u3e7M@z)pcPDcL4|aK+G&(;C+nV8XfMEqa`9o3ra0CM)cAq zL3}aL7o;Hq#w2}ae;3**SZ947da$2?zrwkEf~}C%uOgkN|54jOam>631ss`%&#+Wy z1h6zT#~jMheC7N|n?tBPI8_F~oZJQ;jDr4(CDven4%;t}cl(y-9s`jr9TH1b@j|PJ zo2d<;5XKEZ!hh6DR1PaI!cL~eHWOZYjT7GIQxt*Dw~I@1D#~pH!YkhiG@mbFF5m15 z79wW>eMWT`9+yvUP)ZGT{F+I50dkb_oaLXK<)NBFmH9@(wkG$@11$`eW!6)OCASSM z7#7~NjR#9{8nPIiYk>Eg$gjeVoMghG+y(f~b+mv<|8Tq%@$mMTLlKrr6n233WPda-Ij`+2u}k~e~((M zjD+V`_xgjs1jt|d+`>vmy{Vud%oE5)!LvswsT+1~cl`%Qfs*hB#f1zu_e6a)&_1eB z?DhL1DGWCzEAOW)wu*{c3tO)1nxz( zaeTM$bOc?oLXwQTDBAQOahMsi)cRm+A^e%FB(u!e^$S12?-T3D^J)bs4q{nG!>cTA zk~uUn()*I}4U zBz#xBW%YD-ql9(&VkpU-# zn5tq(oM$ahDG0LFy6wX8bf5ZkcWnYPU5V-Z-Z*=djKFw3loVe1z2$!D;(5n;7$lQ6 z4>q7%efXojo0~DIrqXhtT&XAS=XPi?v{xVmUa@8Nw>SSidOIVV&hJ|IcpO5Z)p+=8 z%e=7pp7F*ywW8%bxCz>#eQ8$SS>fsE_XNVdGFKWAhJ{ud(e+vesz8+} zb1ic>u98>H>DXqDxkSw#(m6stAU@BH~SZl!r& zMaNyT!P3*|Q`{1tRRnfP=@g?kb3eAtVaxeTX~ z@X~n_D0BCks01q?wKAXT^2$&OuYyGEudfUa28RgCu3LO`u)cWtk=}K$#!hU%A#E~4 z0!$aZnfh9enAVd))_w_Q+|G72v*#D%&CWiq;B094x}M^nv0<*o+WgXK=HjH}`4}f> zDq6+V*k<@uSqYr>)aCJ!^8@*-h|weiIID?|0x#U8p%$4Vs`o^ohxXSopI1la4ZGK3 zS{SI?Wai=lVy2=pr>FjSWi2Cjd;ov)M>T9Dj<)g}XX7f^49Wj_@5i*Yj-ST~y5Lqj zfb_d-ye=i-uA-|PtfF5HVuQbaF^gc6eCk0GypEgW=~Nlv17xvF^HFF33Io!4lu)QW zmR3r>f!gn126$)ADO1Z;qQ-4?SG#s1er3uVp?d9NzskQTAl&<1KgLeT#X5)QSJL?^ zRxho&@*qgBFs{Au(U!9BGDwrQ?C@&4>>Ycn=PA2}Icu;MP;0q&;rhlvmvR1nBemk^ z6KQsK#ox5{x2`)kBh_$)%4X+J61NVoiz$V;_0THF~k& z^{gHLWc8~Q9va)Z2jzBL)01Umn|=N-$^iwmYGS zHG>@rE(xMi)LkYPf(OalzEHTWdkdq}Y3<)KF6%J&*$r|{*`nHwHGdZU+a4wGy>aBp zmos!FrZh-r(?INS!(h!e0>S-88mat|c20^GU#0Pk8?orej#&%4Wvl(186YRjh1EY? z=fqqCObJd+?X)LB-Ab0|*_#ks+}Z}XK)VX=(qG|6EC71W-HIgE*!|tqk8^Kq`Naw` zM&oHzD#i^v6BkTK{^6G?Y*ys)kW$)>Bvf<<@V4sLjdJcIyq$gj6qHK9Zb|*pQ9<`s}uP5Qy~q- zBV40e#()3zRfGF5ppN>TRKq{uuZ%LCEAn%&XI6^Y0_Jta1W^wtQm?01BSK$kX$-wP z(3&@K-I;8u-fa(-nQg@&)Oc+AY1j^qq{+n1K54yksn3MJ>sFRjl*n(hX5yJOp{pkS%wSRF8(cJ~y?!*{f>wdBIZa@h9V! zx0iO>pOJ<0+S9%B>PCGLraAbje|?S${fGk8|Kb|-9X%|0Wj7<5)N>y?=>W=X$+E(i z>&!`4d2(uBv#sp*b>14hL#-)(L#_qG)@eGW%hO9~Ci2XJb2M5V8V$H2gnIe;deR!6i;_!t*a}M8yQYTrRL6ExQiF{^L-{0?(jZ>|Rcj z_y27hRM>0IK~#V@Zc6`WL{u0#&=2!4FaPvkV+p2E^_KB_bCmPXa!{RoRZAhLLY<9j z2LJciFm##Z$2W7r{}A$zRsXk*{cGUW?vh?(#5)+E_kR=g|0WV@z!z|aH~+D7=*v3v z*I^a|AOCwy<~4HNLh3qs#jk%qJ=l2>@5f`f|1$u6Ns@a9wE-z{|rE1q&c9h`2WJl7j*xw={V~qf|u8S OUMnM^C|)UM81z4-_$>(l diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/tilted_pie/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/tilted_pie/index.ts deleted file mode 100644 index 21d8ba1e1b04a..0000000000000 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/tilted_pie/index.ts +++ /dev/null @@ -1,23 +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 { ElementFactory } from '../../../types'; -import header from './header.png'; - -export const tiltedPie: ElementFactory = () => ({ - name: 'tiltedPie', - displayName: 'Tilted pie chart', - tags: ['chart', 'proportion'], - width: 500, - height: 250, - help: 'A customizable tilted pie chart', - image: header, - expression: `filters -| demodata -| pointseries color="project" size="max(price)" -| pie tilt=0.5 -| render`, -}); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/time_filter/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/time_filter/header.png deleted file mode 100644 index d36b4cc97e5b13c542cfb59e53013e179532e741..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18970 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 ({ - name: 'time_filter', + name: 'timeFilter', displayName: 'Time filter', - tags: ['filter'], + type: 'filter', help: 'Set a time window', - image: header, + icon: 'calendar', height: 50, expression: `timefilterControl compact=true column=@timestamp | render`, diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/vert_bar_chart/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/vert_bar_chart/header.png deleted file mode 100644 index 90505dd0dc77df0b147956f3d01e2afdecf44140..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17393 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 ASpWb4 diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/vert_bar_chart/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/vert_bar_chart/index.ts index ac4e3a0a72150..2354be0328e7c 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/vert_bar_chart/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/vert_bar_chart/index.ts @@ -5,14 +5,13 @@ */ import { ElementFactory } from '../../../types'; -import header from './header.png'; export const verticalBarChart: ElementFactory = () => ({ name: 'verticalBarChart', displayName: 'Vertical bar chart', - tags: ['chart'], + type: 'chart', help: 'A customizable vertical bar chart', - image: header, + icon: 'visBarVertical', expression: `filters | demodata | pointseries x="project" y="size(cost)" color="project" diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/vertical_progress_bar/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/vertical_progress_bar/header.png deleted file mode 100644 index b9ff963e92c3180182d4a2edc96789ad2a348718..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15098 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 P7lfL!rc&W~i<|!k)4A}A diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/vertical_progress_bar/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/vertical_progress_bar/index.ts index e12903dafede9..b5e6c9816e3f5 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/vertical_progress_bar/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/vertical_progress_bar/index.ts @@ -6,16 +6,14 @@ import { openSans } from '../../../common/lib/fonts'; import { ElementFactory } from '../../../types'; -import header from './header.png'; export const verticalProgressBar: ElementFactory = () => ({ name: 'verticalProgressBar', displayName: 'Vertical progress bar', - tags: ['chart', 'proportion'], + type: 'progress', help: 'Displays progress as a portion of a vertical bar', width: 80, height: 400, - image: header, expression: `filters | demodata | math "mean(percent_uptime)" diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/vertical_progress_pill/header.png b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/vertical_progress_pill/header.png deleted file mode 100644 index a4ac6b57da236b9c75ce830b8ff8b14d9ac6b291..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20611 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⪻e9cqk=1RY6 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 diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/vertical_progress_pill/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/vertical_progress_pill/index.ts index 8926a12da8a47..28e80372494db 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/vertical_progress_pill/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/vertical_progress_pill/index.ts @@ -6,16 +6,14 @@ import { openSans } from '../../../common/lib/fonts'; import { ElementFactory } from '../../../types'; -import header from './header.png'; export const verticalProgressPill: ElementFactory = () => ({ name: 'verticalProgressPill', displayName: 'Vertical progress pill', - tags: ['chart', 'proportion'], + type: 'progress', help: 'Displays progress as a portion of a vertical pill', width: 80, height: 400, - image: header, expression: `filters | demodata | math "mean(percent_uptime)" diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/chart.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/chart.ts deleted file mode 100644 index 4c535a42c3c44..0000000000000 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/chart.ts +++ /dev/null @@ -1,15 +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 { euiPaletteColorBlind } from '@elastic/eui'; -import { TagFactory } from '../../../public/lib/tag'; -import { TagStrings as strings } from '../../../i18n'; -const euiVisPalette = euiPaletteColorBlind(); - -export const chart: TagFactory = () => ({ - name: strings.chart(), - color: euiVisPalette[4], -}); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/filter.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/filter.ts deleted file mode 100644 index 5249856dec271..0000000000000 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/filter.ts +++ /dev/null @@ -1,16 +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 { euiPaletteColorBlind } from '@elastic/eui'; -import { TagFactory } from '../../../public/lib/tag'; -import { TagStrings as strings } from '../../../i18n'; - -const euiVisPalette = euiPaletteColorBlind(); - -export const filter: TagFactory = () => ({ - name: strings.filter(), - color: euiVisPalette[1], -}); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/graphic.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/graphic.ts deleted file mode 100644 index 36d66801ef681..0000000000000 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/graphic.ts +++ /dev/null @@ -1,15 +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 { euiPaletteColorBlind } from '@elastic/eui'; -import { TagFactory } from '../../../public/lib/tag'; -import { TagStrings as strings } from '../../../i18n'; -const euiVisPalette = euiPaletteColorBlind(); - -export const graphic: TagFactory = () => ({ - name: strings.graphic(), - color: euiVisPalette[5], -}); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/index.ts index 2587665a452b5..2e711437c72a8 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/index.ts @@ -4,13 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { chart } from './chart'; -import { filter } from './filter'; -import { graphic } from './graphic'; import { presentation } from './presentation'; -import { proportion } from './proportion'; import { report } from './report'; -import { text } from './text'; // Registry expects a function that returns a spec object -export const tagSpecs = [chart, filter, graphic, presentation, proportion, report, text]; +export const tagSpecs = [presentation, report]; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/proportion.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/proportion.ts deleted file mode 100644 index 4d37ecfaa367a..0000000000000 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/proportion.ts +++ /dev/null @@ -1,15 +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 { euiPaletteColorBlind } from '@elastic/eui'; -import { TagFactory } from '../../../public/lib/tag'; -import { TagStrings as strings } from '../../../i18n'; -const euiVisPalette = euiPaletteColorBlind(); - -export const proportion: TagFactory = () => ({ - name: strings.proportion(), - color: euiVisPalette[3], -}); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/text.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/text.ts deleted file mode 100644 index f9faf2ad2e3ca..0000000000000 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/text.ts +++ /dev/null @@ -1,13 +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 { TagFactory } from '../../../public/lib/tag'; -import { TagStrings as strings } from '../../../i18n'; - -export const text: TagFactory = () => ({ - name: strings.text(), - color: '#D3DAE6', -}); diff --git a/x-pack/legacy/plugins/canvas/common/lib/constants.ts b/x-pack/legacy/plugins/canvas/common/lib/constants.ts index ac8e80b8d7b89..a37dc3fd6a7b3 100644 --- a/x-pack/legacy/plugins/canvas/common/lib/constants.ts +++ b/x-pack/legacy/plugins/canvas/common/lib/constants.ts @@ -40,3 +40,4 @@ export const API_ROUTE_SHAREABLE_ZIP = '/public/canvas/zip'; export const API_ROUTE_SHAREABLE_RUNTIME = '/public/canvas/runtime'; export const API_ROUTE_SHAREABLE_RUNTIME_DOWNLOAD = `/public/canvas/${SHAREABLE_RUNTIME_NAME}.js`; export const CANVAS_EMBEDDABLE_CLASSNAME = `canvasEmbeddable`; +export const CONTEXT_MENU_TOP_BORDER_CLASSNAME = 'canvasContextMenu--topBorder'; diff --git a/x-pack/legacy/plugins/canvas/i18n/components.ts b/x-pack/legacy/plugins/canvas/i18n/components.ts index d0a9051d7af87..7bd16c4933ce1 100644 --- a/x-pack/legacy/plugins/canvas/i18n/components.ts +++ b/x-pack/legacy/plugins/canvas/i18n/components.ts @@ -15,7 +15,7 @@ export const ComponentStrings = { }), getTitleText: () => i18n.translate('xpack.canvas.embedObject.titleText', { - defaultMessage: 'Embed Object', + defaultMessage: 'Add from Visualize library', }), }, AdvancedFilter: { @@ -305,21 +305,21 @@ export const ComponentStrings = { }), }, ElementControls: { - getEditTooltip: () => - i18n.translate('xpack.canvas.elementControls.editToolTip', { - defaultMessage: 'Edit', - }), - getEditAriaLabel: () => - i18n.translate('xpack.canvas.elementControls.editAriaLabel', { - defaultMessage: 'Edit element', + getDeleteAriaLabel: () => + i18n.translate('xpack.canvas.elementControls.deleteAriaLabel', { + defaultMessage: 'Delete element', }), getDeleteTooltip: () => i18n.translate('xpack.canvas.elementControls.deleteToolTip', { defaultMessage: 'Delete', }), - getDeleteAriaLabel: () => - i18n.translate('xpack.canvas.elementControls.deleteAriaLabel', { - defaultMessage: 'Delete element', + getEditAriaLabel: () => + i18n.translate('xpack.canvas.elementControls.editAriaLabel', { + defaultMessage: 'Edit element', + }), + getEditTooltip: () => + i18n.translate('xpack.canvas.elementControls.editToolTip', { + defaultMessage: 'Edit', }), }, ElementSettings: { @@ -336,53 +336,6 @@ export const ComponentStrings = { description: 'This tab contains the settings for how data is displayed in a Canvas element', }), }, - ElementTypes: { - getEditElementTitle: () => - i18n.translate('xpack.canvas.elementTypes.editElementTitle', { - defaultMessage: 'Edit element', - }), - getDeleteElementTitle: (elementName: string) => - i18n.translate('xpack.canvas.elementTypes.deleteElementTitle', { - defaultMessage: `Delete element '{elementName}'?`, - values: { - elementName, - }, - }), - getDeleteElementDescription: () => - i18n.translate('xpack.canvas.elementTypes.deleteElementDescription', { - defaultMessage: 'Are you sure you want to delete this element?', - }), - getCancelButtonLabel: () => - i18n.translate('xpack.canvas.elementTypes.cancelButtonLabel', { - defaultMessage: 'Cancel', - }), - getDeleteButtonLabel: () => - i18n.translate('xpack.canvas.elementTypes.deleteButtonLabel', { - defaultMessage: 'Delete', - }), - getAddNewElementTitle: () => - i18n.translate('xpack.canvas.elementTypes.addNewElementTitle', { - defaultMessage: 'Add new elements', - }), - getAddNewElementDescription: () => - i18n.translate('xpack.canvas.elementTypes.addNewElementDescription', { - defaultMessage: 'Group and save workpad elements to create new elements', - }), - getFindElementPlaceholder: () => - i18n.translate('xpack.canvas.elementTypes.findElementPlaceholder', { - defaultMessage: 'Find element', - }), - getElementsTitle: () => - i18n.translate('xpack.canvas.elementTypes.elementsTitle', { - defaultMessage: 'Elements', - description: 'Title for the "Elements" tab when adding a new element', - }), - getMyElementsTitle: () => - i18n.translate('xpack.canvas.elementTypes.myElementsTitle', { - defaultMessage: 'My elements', - description: 'Title for the "My elements" tab when adding a new element', - }), - }, Error: { getDescription: () => i18n.translate('xpack.canvas.errorComponent.description', { @@ -633,6 +586,61 @@ export const ComponentStrings = { defaultMessage: 'Delete', }), }, + SavedElementsModal: { + getAddNewElementDescription: () => + i18n.translate('xpack.canvas.savedElementsModal.addNewElementDescription', { + defaultMessage: 'Group and save workpad elements to create new elements', + }), + getAddNewElementTitle: () => + i18n.translate('xpack.canvas.savedElementsModal.addNewElementTitle', { + defaultMessage: 'Add new elements', + }), + getCancelButtonLabel: () => + i18n.translate('xpack.canvas.savedElementsModal.cancelButtonLabel', { + defaultMessage: 'Cancel', + }), + getDeleteButtonLabel: () => + i18n.translate('xpack.canvas.savedElementsModal.deleteButtonLabel', { + defaultMessage: 'Delete', + }), + getDeleteElementDescription: () => + i18n.translate('xpack.canvas.savedElementsModal.deleteElementDescription', { + defaultMessage: 'Are you sure you want to delete this element?', + }), + getDeleteElementTitle: (elementName: string) => + i18n.translate('xpack.canvas.savedElementsModal.deleteElementTitle', { + defaultMessage: `Delete element '{elementName}'?`, + values: { + elementName, + }, + }), + getEditElementTitle: () => + i18n.translate('xpack.canvas.savedElementsModal.editElementTitle', { + defaultMessage: 'Edit element', + }), + getElementsTitle: () => + i18n.translate('xpack.canvas.savedElementsModal.elementsTitle', { + defaultMessage: 'Elements', + description: 'Title for the "Elements" tab when adding a new element', + }), + getFindElementPlaceholder: () => + i18n.translate('xpack.canvas.savedElementsModal.findElementPlaceholder', { + defaultMessage: 'Find element', + }), + getModalTitle: () => + i18n.translate('xpack.canvas.savedElementsModal.modalTitle', { + defaultMessage: 'My elements', + }), + getMyElementsTitle: () => + i18n.translate('xpack.canvas.savedElementsModal.myElementsTitle', { + defaultMessage: 'My elements', + description: 'Title for the "My elements" tab when adding a new element', + }), + getSavedElementsModalCloseButtonLabel: () => + i18n.translate('xpack.canvas.workpadHeader.addElementModalCloseButtonLabel', { + defaultMessage: 'Close', + }), + }, ShareWebsiteFlyout: { getRuntimeStepTitle: () => i18n.translate('xpack.canvas.shareWebsiteFlyout.snippetsStep.downloadRuntimeTitle', { @@ -652,7 +660,7 @@ export const ComponentStrings = { defaultMessage: 'Share on a website', }), getUnsupportedRendererWarning: () => - i18n.translate('xpack.canvas.workpadHeaderWorkpadExport.unsupportedRendererWarning', { + i18n.translate('xpack.canvas.workpadHeaderShareMenu.unsupportedRendererWarning', { defaultMessage: 'This workpad contains render functions that are not supported by the {CANVAS} Shareable Workpad Runtime. These elements will not be rendered:', values: { @@ -900,6 +908,10 @@ export const ComponentStrings = { i18n.translate('xpack.canvas.textStylePicker.alignRightOption', { defaultMessage: 'Align right', }), + getFontColorLabel: () => + i18n.translate('xpack.canvas.textStylePicker.fontColorLabel', { + defaultMessage: 'Font Color', + }), getStyleBoldOption: () => i18n.translate('xpack.canvas.textStylePicker.styleBoldOption', { defaultMessage: 'Bold', @@ -912,10 +924,6 @@ export const ComponentStrings = { i18n.translate('xpack.canvas.textStylePicker.styleUnderlineOption', { defaultMessage: 'Underline', }), - getFontColorLabel: () => - i18n.translate('xpack.canvas.textStylePicker.fontColorLabel', { - defaultMessage: 'Font Color', - }), }, TimePicker: { getApplyButtonLabel: () => @@ -962,6 +970,10 @@ export const ComponentStrings = { description: '"stylesheet" refers to the collection of CSS style rules entered by the user.', }), + getBackgroundColorLabel: () => + i18n.translate('xpack.canvas.workpadConfig.backgroundColorLabel', { + defaultMessage: 'Background color', + }), getFlipDimensionAriaLabel: () => i18n.translate('xpack.canvas.workpadConfig.swapDimensionsAriaLabel', { defaultMessage: `Swap the page's width and height`, @@ -1013,10 +1025,6 @@ export const ComponentStrings = { defaultMessage: 'US Letter', description: 'This is referring to the dimensions of U.S. standard letter paper.', }), - getBackgroundColorLabel: () => - i18n.translate('xpack.canvas.workpadConfig.backgroundColorLabel', { - defaultMessage: 'Background color', - }), }, WorkpadCreate: { getWorkpadCreateButtonLabel: () => @@ -1029,14 +1037,6 @@ export const ComponentStrings = { i18n.translate('xpack.canvas.workpadHeader.addElementButtonLabel', { defaultMessage: 'Add element', }), - getAddElementModalCloseButtonLabel: () => - i18n.translate('xpack.canvas.workpadHeader.addElementModalCloseButtonLabel', { - defaultMessage: 'Close', - }), - getEmbedObjectButtonLabel: () => - i18n.translate('xpack.canvas.workpadHeader.embedObjectButtonLabel', { - defaultMessage: 'Embed object', - }), getFullScreenButtonAriaLabel: () => i18n.translate('xpack.canvas.workpadHeader.fullscreenButtonAriaLabel', { defaultMessage: 'View fullscreen', @@ -1080,9 +1080,9 @@ export const ComponentStrings = { }), }, WorkpadHeaderControlSettings: { - getTooltip: () => - i18n.translate('xpack.canvas.workpadHeaderControlSettings.settingsTooltip', { - defaultMessage: 'Control settings', + getButtonLabel: () => + i18n.translate('xpack.canvas.workpadHeaderControlSettings.buttonLabel', { + defaultMessage: 'Options', }), }, WorkpadHeaderCustomInterval: { @@ -1105,6 +1105,56 @@ export const ComponentStrings = { defaultMessage: 'Set a custom interval', }), }, + WorkpadHeaderElementMenu: { + getAssetsMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderElementMenu.manageAssetsMenuItemLabel', { + defaultMessage: 'Manage assets', + }), + getChartMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderElementMenu.chartMenuItemLabel', { + defaultMessage: 'Chart', + }), + getElementMenuButtonLabel: () => + i18n.translate('xpack.canvas.workpadHeaderElementMenu.elementMenuButtonLabel', { + defaultMessage: 'Add element', + }), + getElementMenuLabel: () => + i18n.translate('xpack.canvas.workpadHeaderElementMenu.elementMenuLabel', { + defaultMessage: 'Add an element', + }), + getEmbedObjectMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderElementMenu.embedObjectMenuItemLabel', { + defaultMessage: 'Add from Visualize library', + }), + getFilterMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderElementMenu.filterMenuItemLabel', { + defaultMessage: 'Filter', + }), + getImageMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderElementMenu.imageMenuItemLabel', { + defaultMessage: 'Image', + }), + getMyElementsMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderElementMenu.myElementsMenuItemLabel', { + defaultMessage: 'My elements', + }), + getOtherMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderElementMenu.otherMenuItemLabel', { + defaultMessage: 'Other', + }), + getProgressMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderElementMenu.progressMenuItemLabel', { + defaultMessage: 'Progress', + }), + getShapeMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderElementMenu.shapeMenuItemLabel', { + defaultMessage: 'Shape', + }), + getTextMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderElementMenu.textMenuItemLabel', { + defaultMessage: 'Text', + }), + }, WorkpadHeaderKioskControls: { getCycleFormLabel: () => i18n.translate('xpack.canvas.workpadHeaderKioskControl.cycleFormLabel', { @@ -1129,9 +1179,9 @@ export const ComponentStrings = { defaultMessage: 'Refresh data', }), }, - WorkpadHeaderWorkpadExport: { + WorkpadHeaderShareMenu: { getCopyPDFMessage: () => - i18n.translate('xpack.canvas.workpadHeaderWorkpadExport.copyPDFMessage', { + i18n.translate('xpack.canvas.workpadHeaderShareMenu.copyPDFMessage', { defaultMessage: 'The {PDF} generation {URL} was copied to your clipboard.', values: { PDF, @@ -1139,15 +1189,15 @@ export const ComponentStrings = { }, }), getCopyReportingConfigMessage: () => - i18n.translate('xpack.canvas.workpadHeaderWorkpadExport.copyReportingConfigMessage', { + i18n.translate('xpack.canvas.workpadHeaderShareMenu.copyReportingConfigMessage', { defaultMessage: 'Copied reporting configuration to clipboard', }), getCopyShareConfigMessage: () => - i18n.translate('xpack.canvas.workpadHeaderWorkpadExport.copyShareConfigMessage', { + i18n.translate('xpack.canvas.workpadHeaderShareMenu.copyShareConfigMessage', { defaultMessage: 'Copied share markup to clipboard', }), getExportPDFErrorTitle: (workpadName: string) => - i18n.translate('xpack.canvas.workpadHeaderWorkpadExport.exportPDFErrorMessage', { + i18n.translate('xpack.canvas.workpadHeaderShareMenu.exportPDFErrorMessage', { defaultMessage: "Failed to create {PDF} for '{workpadName}'", values: { PDF, @@ -1155,14 +1205,14 @@ export const ComponentStrings = { }, }), getExportPDFMessage: () => - i18n.translate('xpack.canvas.workpadHeaderWorkpadExport.exportPDFMessage', { + i18n.translate('xpack.canvas.workpadHeaderShareMenu.exportPDFMessage', { defaultMessage: 'Exporting {PDF}. You can track the progress in Management.', values: { PDF, }, }), getExportPDFTitle: (workpadName: string) => - i18n.translate('xpack.canvas.workpadHeaderWorkpadExport.exportPDFTitle', { + i18n.translate('xpack.canvas.workpadHeaderShareMenu.exportPDFTitle', { defaultMessage: "{PDF} export of workpad '{workpadName}'", values: { PDF, @@ -1170,7 +1220,7 @@ export const ComponentStrings = { }, }), getPDFPanelCopyAriaLabel: () => - i18n.translate('xpack.canvas.workpadHeaderWorkpadExport.pdfPanelCopyAriaLabel', { + i18n.translate('xpack.canvas.workpadHeaderShareMenu.pdfPanelCopyAriaLabel', { defaultMessage: 'Alternatively, you can generate a {PDF} from a script or with Watcher by using this {URL}. Press Enter to copy the {URL} to clipboard.', values: { @@ -1179,7 +1229,7 @@ export const ComponentStrings = { }, }), getPDFPanelCopyButtonLabel: () => - i18n.translate('xpack.canvas.workpadHeaderWorkpadExport.pdfPanelCopyButtonLabel', { + i18n.translate('xpack.canvas.workpadHeaderShareMenu.pdfPanelCopyButtonLabel', { defaultMessage: 'Copy {POST} {URL}', values: { POST, @@ -1187,7 +1237,7 @@ export const ComponentStrings = { }, }), getPDFPanelCopyDescription: () => - i18n.translate('xpack.canvas.workpadHeaderWorkpadExport.pdfPanelCopyDescription', { + i18n.translate('xpack.canvas.workpadHeaderShareMenu.pdfPanelCopyDescription', { defaultMessage: 'Alternatively, copy this {POST} {URL} to call generation from outside {KIBANA} or from Watcher.', values: { @@ -1197,14 +1247,14 @@ export const ComponentStrings = { }, }), getPDFPanelGenerateButtonLabel: () => - i18n.translate('xpack.canvas.workpadHeaderWorkpadExport.pdfPanelGenerateButtonLabel', { + i18n.translate('xpack.canvas.workpadHeaderShareMenu.pdfPanelGenerateButtonLabel', { defaultMessage: 'Generate {PDF}', values: { PDF, }, }), getPDFPanelGenerateDescription: () => - i18n.translate('xpack.canvas.workpadHeaderWorkpadExport.pdfPanelGenerateDescription', { + i18n.translate('xpack.canvas.workpadHeaderShareMenu.pdfPanelGenerateDescription', { defaultMessage: '{PDF}s can take a minute or two to generate based on the size of your workpad.', values: { @@ -1212,7 +1262,7 @@ export const ComponentStrings = { }, }), getShareableZipErrorTitle: (workpadName: string) => - i18n.translate('xpack.canvas.workpadHeaderWorkpadExport.shareWebsiteErrorTitle', { + i18n.translate('xpack.canvas.workpadHeaderShareMenu.shareWebsiteErrorTitle', { defaultMessage: "Failed to create {ZIP} file for '{workpadName}'. The workpad may be too large. You'll need to download the files separately.", values: { @@ -1221,69 +1271,101 @@ export const ComponentStrings = { }, }), getShareDownloadJSONTitle: () => - i18n.translate('xpack.canvas.workpadHeaderWorkpadExport.shareDownloadJSONTitle', { + i18n.translate('xpack.canvas.workpadHeaderShareMenu.shareDownloadJSONTitle', { defaultMessage: 'Download as {JSON}', values: { JSON, }, }), getShareDownloadPDFTitle: () => - i18n.translate('xpack.canvas.workpadHeaderWorkpadExport.shareDownloadPDFTitle', { + i18n.translate('xpack.canvas.workpadHeaderShareMenu.shareDownloadPDFTitle', { defaultMessage: '{PDF} reports', values: { PDF, }, }), + getShareMenuButtonLabel: () => + i18n.translate('xpack.canvas.workpadHeaderShareMenu.shareMenuButtonLabel', { + defaultMessage: 'Share', + }), getShareWebsiteTitle: () => - i18n.translate('xpack.canvas.workpadHeaderWorkpadExport.shareWebsiteTitle', { + i18n.translate('xpack.canvas.workpadHeaderShareMenu.shareWebsiteTitle', { defaultMessage: 'Share on a website', }), getShareWorkpadMessage: () => - i18n.translate('xpack.canvas.workpadHeaderWorkpadExport.shareWorkpadMessage', { + i18n.translate('xpack.canvas.workpadHeaderShareMenu.shareWorkpadMessage', { defaultMessage: 'Share this workpad', }), getUnknownExportErrorMessage: (type: string) => - i18n.translate('xpack.canvas.workpadHeaderWorkpadExport.unknownExportErrorMessage', { + i18n.translate('xpack.canvas.workpadHeaderShareMenu.unknownExportErrorMessage', { defaultMessage: 'Unknown export type: {type}', values: { type, }, }), }, - WorkpadHeaderWorkpadZoom: { + WorkpadHeaderViewMenu: { + getFullscreenMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderViewMenu.fullscreenMenuLabel', { + defaultMessage: 'Enter fullscreen mode', + }), + getHideEditModeLabel: () => + i18n.translate('xpack.canvas.workpadHeaderViewMenu.hideEditModeLabel', { + defaultMessage: 'Hide editing controls', + }), + getRefreshMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderViewMenu.refreshMenuItemLabel', { + defaultMessage: 'Refresh data', + }), + getShowEditModeLabel: () => + i18n.translate('xpack.canvas.workpadHeaderViewMenu.showEditModeLabel', { + defaultMessage: 'Show editing controls', + }), + getViewMenuButtonLabel: () => + i18n.translate('xpack.canvas.workpadHeaderViewMenu.viewMenuButtonLabel', { + defaultMessage: 'View', + }), + getViewMenuLabel: () => + i18n.translate('xpack.canvas.workpadHeaderViewMenu.viewMenuLabel', { + defaultMessage: 'View options', + }), getZoomControlsAriaLabel: () => - i18n.translate('xpack.canvas.workpadHeaderWorkpadZoom.zoomControlsAriaLabel', { + i18n.translate('xpack.canvas.workpadHeaderViewMenu.zoomControlsAriaLabel', { defaultMessage: 'Zoom controls', }), getZoomControlsTooltip: () => - i18n.translate('xpack.canvas.workpadHeaderWorkpadZoom.zoomControlsTooltip', { + i18n.translate('xpack.canvas.workpadHeaderViewMenu.zoomControlsTooltip', { defaultMessage: 'Zoom controls', }), getZoomFitToWindowText: () => - i18n.translate('xpack.canvas.workpadHeaderWorkpadZoom.zoomFitToWindowText', { + i18n.translate('xpack.canvas.workpadHeaderViewMenu.zoomFitToWindowText', { defaultMessage: 'Fit to window', }), getZoomInText: () => - i18n.translate('xpack.canvas.workpadHeaderWorkpadZoom.zoomInText', { + i18n.translate('xpack.canvas.workpadHeaderViewMenu.zoomInText', { defaultMessage: 'Zoom in', }), + getZoomMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderViewMenu.zoomMenuItemLabel', { + defaultMessage: 'Zoom', + }), getZoomOutText: () => - i18n.translate('xpack.canvas.workpadHeaderWorkpadZoom.zoomOutText', { + i18n.translate('xpack.canvas.workpadHeaderViewMenu.zoomOutText', { defaultMessage: 'Zoom out', }), getZoomPanelTitle: () => - i18n.translate('xpack.canvas.workpadHeaderWorkpadZoom.zoomPanelTitle', { + i18n.translate('xpack.canvas.workpadHeaderViewMenu.zoomPanelTitle', { defaultMessage: 'Zoom', }), getZoomPercentage: (scale: number) => - i18n.translate('xpack.canvas.workpadHeaderWorkpadZoom.zoomResetText', { + i18n.translate('xpack.canvas.workpadHeaderViewMenu.zoomResetText', { defaultMessage: '{scalePercentage}%', values: { scalePercentage: scale * 100, }, }), getZoomResetText: () => - i18n.translate('xpack.canvas.workpadHeaderWorkpadZoom.zoomPrecentageValue', { + i18n.translate('xpack.canvas.workpadHeaderViewMenu.zoomPrecentageValue', { defaultMessage: 'Reset', }), }, diff --git a/x-pack/legacy/plugins/canvas/i18n/elements/apply_strings.ts b/x-pack/legacy/plugins/canvas/i18n/elements/apply_strings.ts index 41f88a3c75f90..4464ed5dbf185 100644 --- a/x-pack/legacy/plugins/canvas/i18n/elements/apply_strings.ts +++ b/x-pack/legacy/plugins/canvas/i18n/elements/apply_strings.ts @@ -7,8 +7,6 @@ import { ElementFactory } from '../../types'; import { getElementStrings } from './index'; -import { TagStrings } from '../../i18n'; - /** * This function takes a set of Canvas Element specification factories, runs them, * replaces relevant strings (if available) and returns a new factory. We do this @@ -34,17 +32,6 @@ export const applyElementStrings = (elements: ElementFactory[]) => { if (displayName) { result.displayName = displayName; } - - // Set translated tags - if (result.tags) { - result.tags = result.tags.map(tag => { - if (tag in TagStrings) { - return TagStrings[tag](); - } - - return tag; - }); - } } return () => result; diff --git a/x-pack/legacy/plugins/canvas/i18n/elements/element_strings.test.ts b/x-pack/legacy/plugins/canvas/i18n/elements/element_strings.test.ts index ce908ac6c5566..c28229bdab33f 100644 --- a/x-pack/legacy/plugins/canvas/i18n/elements/element_strings.test.ts +++ b/x-pack/legacy/plugins/canvas/i18n/elements/element_strings.test.ts @@ -9,8 +9,6 @@ import { coreMock } from '../../../../../../src/core/public/mocks'; const elementSpecs = initializeElements(coreMock.createSetup() as any, {} as any); -import { TagStrings } from '../tags'; - describe('ElementStrings', () => { const elementStrings = getElementStrings(); const elementNames = elementSpecs.map(spec => spec().name); @@ -37,15 +35,4 @@ describe('ElementStrings', () => { expect(value).toHaveProperty('help'); }); }); - - test('All elements should have tags that are defined', () => { - const tagNames = Object.keys(TagStrings); - - elementSpecs.forEach(spec => { - const element = spec(); - if (element.tags) { - element.tags.forEach((tagName: string) => expect(tagNames).toContain(tagName)); - } - }); - }); }); diff --git a/x-pack/legacy/plugins/canvas/i18n/elements/element_strings.ts b/x-pack/legacy/plugins/canvas/i18n/elements/element_strings.ts index df23dff1c7127..595ef4cb92206 100644 --- a/x-pack/legacy/plugins/canvas/i18n/elements/element_strings.ts +++ b/x-pack/legacy/plugins/canvas/i18n/elements/element_strings.ts @@ -23,7 +23,7 @@ interface ElementStringDict { export const getElementStrings = (): ElementStringDict => ({ areaChart: { displayName: i18n.translate('xpack.canvas.elements.areaChartDisplayName', { - defaultMessage: 'Area chart', + defaultMessage: 'Area', }), help: i18n.translate('xpack.canvas.elements.areaChartHelpText', { defaultMessage: 'A line chart with a filled body', @@ -31,7 +31,7 @@ export const getElementStrings = (): ElementStringDict => ({ }, bubbleChart: { displayName: i18n.translate('xpack.canvas.elements.bubbleChartDisplayName', { - defaultMessage: 'Bubble chart', + defaultMessage: 'Bubble', }), help: i18n.translate('xpack.canvas.elements.bubbleChartHelpText', { defaultMessage: 'A customizable bubble chart', @@ -39,31 +39,31 @@ export const getElementStrings = (): ElementStringDict => ({ }, debug: { displayName: i18n.translate('xpack.canvas.elements.debugDisplayName', { - defaultMessage: 'Debug', + defaultMessage: 'Debug data', }), help: i18n.translate('xpack.canvas.elements.debugHelpText', { defaultMessage: 'Just dumps the configuration of the element', }), }, - donut: { - displayName: i18n.translate('xpack.canvas.elements.donutChartDisplayName', { - defaultMessage: 'Donut chart', - }), - help: i18n.translate('xpack.canvas.elements.donutChartHelpText', { - defaultMessage: 'A customizable donut chart', - }), - }, - dropdown_filter: { + dropdownFilter: { displayName: i18n.translate('xpack.canvas.elements.dropdownFilterDisplayName', { - defaultMessage: 'Dropdown filter', + defaultMessage: 'Dropdown select', }), help: i18n.translate('xpack.canvas.elements.dropdownFilterHelpText', { defaultMessage: 'A dropdown from which you can select values for an "exactly" filter', }), }, + filterDebug: { + displayName: i18n.translate('xpack.canvas.elements.filterDebugDisplayName', { + defaultMessage: 'Debug filters', + }), + help: i18n.translate('xpack.canvas.elements.filterDebugHelpText', { + defaultMessage: 'Shows the underlying global filters in a workpad', + }), + }, horizontalBarChart: { displayName: i18n.translate('xpack.canvas.elements.horizontalBarChartDisplayName', { - defaultMessage: 'Horizontal bar chart', + defaultMessage: 'Horizontal bar', }), help: i18n.translate('xpack.canvas.elements.horizontalBarChartHelpText', { defaultMessage: 'A customizable horizontal bar chart', @@ -71,7 +71,7 @@ export const getElementStrings = (): ElementStringDict => ({ }, horizontalProgressBar: { displayName: i18n.translate('xpack.canvas.elements.horizontalProgressBarDisplayName', { - defaultMessage: 'Horizontal progress bar', + defaultMessage: 'Horizontal bar', }), help: i18n.translate('xpack.canvas.elements.horizontalProgressBarHelpText', { defaultMessage: 'Displays progress as a portion of a horizontal bar', @@ -79,7 +79,7 @@ export const getElementStrings = (): ElementStringDict => ({ }, horizontalProgressPill: { displayName: i18n.translate('xpack.canvas.elements.horizontalProgressPillDisplayName', { - defaultMessage: 'Horizontal progress pill', + defaultMessage: 'Horizontal pill', }), help: i18n.translate('xpack.canvas.elements.horizontalProgressPillHelpText', { defaultMessage: 'Displays progress as a portion of a horizontal pill', @@ -95,7 +95,7 @@ export const getElementStrings = (): ElementStringDict => ({ }, lineChart: { displayName: i18n.translate('xpack.canvas.elements.lineChartDisplayName', { - defaultMessage: 'Line chart', + defaultMessage: 'Line', }), help: i18n.translate('xpack.canvas.elements.lineChartHelpText', { defaultMessage: 'A customizable line chart', @@ -119,7 +119,7 @@ export const getElementStrings = (): ElementStringDict => ({ }, pie: { displayName: i18n.translate('xpack.canvas.elements.pieDisplayName', { - defaultMessage: 'Pie chart', + defaultMessage: 'Pie', }), help: i18n.translate('xpack.canvas.elements.pieHelpText', { defaultMessage: 'Pie chart', @@ -135,7 +135,7 @@ export const getElementStrings = (): ElementStringDict => ({ }, progressGauge: { displayName: i18n.translate('xpack.canvas.elements.progressGaugeDisplayName', { - defaultMessage: 'Progress gauge', + defaultMessage: 'Gauge', }), help: i18n.translate('xpack.canvas.elements.progressGaugeHelpText', { defaultMessage: 'Displays progress as a portion of a gauge', @@ -143,7 +143,7 @@ export const getElementStrings = (): ElementStringDict => ({ }, progressSemicircle: { displayName: i18n.translate('xpack.canvas.elements.progressSemicircleDisplayName', { - defaultMessage: 'Progress semicircle', + defaultMessage: 'Semicircle', }), help: i18n.translate('xpack.canvas.elements.progressSemicircleHelpText', { defaultMessage: 'Displays progress as a portion of a semicircle', @@ -151,7 +151,7 @@ export const getElementStrings = (): ElementStringDict => ({ }, progressWheel: { displayName: i18n.translate('xpack.canvas.elements.progressWheelDisplayName', { - defaultMessage: 'Progress wheel', + defaultMessage: 'Wheel', }), help: i18n.translate('xpack.canvas.elements.progressWheelHelpText', { defaultMessage: 'Displays progress as a portion of a wheel', @@ -189,15 +189,7 @@ export const getElementStrings = (): ElementStringDict => ({ defaultMessage: 'A scrollable grid for displaying data in a tabular format', }), }, - tiltedPie: { - displayName: i18n.translate('xpack.canvas.elements.tiltedPieDisplayName', { - defaultMessage: 'Tilted pie chart', - }), - help: i18n.translate('xpack.canvas.elements.tiltedPieHelpText', { - defaultMessage: 'A customizable tilted pie chart', - }), - }, - time_filter: { + timeFilter: { displayName: i18n.translate('xpack.canvas.elements.timeFilterDisplayName', { defaultMessage: 'Time filter', }), @@ -207,7 +199,7 @@ export const getElementStrings = (): ElementStringDict => ({ }, verticalBarChart: { displayName: i18n.translate('xpack.canvas.elements.verticalBarChartDisplayName', { - defaultMessage: 'Vertical bar chart', + defaultMessage: 'Vertical bar', }), help: i18n.translate('xpack.canvas.elements.verticalBarChartHelpText', { defaultMessage: 'A customizable vertical bar chart', @@ -215,7 +207,7 @@ export const getElementStrings = (): ElementStringDict => ({ }, verticalProgressBar: { displayName: i18n.translate('xpack.canvas.elements.verticalProgressBarDisplayName', { - defaultMessage: 'Vertical progress bar', + defaultMessage: 'Vertical bar', }), help: i18n.translate('xpack.canvas.elements.verticalProgressBarHelpText', { defaultMessage: 'Displays progress as a portion of a vertical bar', @@ -223,7 +215,7 @@ export const getElementStrings = (): ElementStringDict => ({ }, verticalProgressPill: { displayName: i18n.translate('xpack.canvas.elements.verticalProgressPillDisplayName', { - defaultMessage: 'Vertical progress pill', + defaultMessage: 'Vertical pill', }), help: i18n.translate('xpack.canvas.elements.verticalProgressPillHelpText', { defaultMessage: 'Displays progress as a portion of a vertical pill', diff --git a/x-pack/legacy/plugins/canvas/i18n/functions/dict/alter_column.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/alter_column.ts index 836c7395ac448..cc601b0ea0e31 100644 --- a/x-pack/legacy/plugins/canvas/i18n/functions/dict/alter_column.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/alter_column.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { alterColumn } from '../../../canvas_plugin_src/functions/common/alterColumn'; import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { DATATABLE_COLUMN_TYPES } from '../../../common/lib'; +import { DATATABLE_COLUMN_TYPES } from '../../../common/lib/constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.alterColumnHelpText', { diff --git a/x-pack/legacy/plugins/canvas/i18n/tags.ts b/x-pack/legacy/plugins/canvas/i18n/tags.ts index 41007c58d738d..9595554260297 100644 --- a/x-pack/legacy/plugins/canvas/i18n/tags.ts +++ b/x-pack/legacy/plugins/canvas/i18n/tags.ts @@ -7,32 +7,12 @@ import { i18n } from '@kbn/i18n'; export const TagStrings: { [key: string]: () => string } = { - chart: () => - i18n.translate('xpack.canvas.tags.chartTag', { - defaultMessage: 'chart', - }), - filter: () => - i18n.translate('xpack.canvas.tags.filterTag', { - defaultMessage: 'filter', - }), - graphic: () => - i18n.translate('xpack.canvas.tags.graphicTag', { - defaultMessage: 'graphic', - }), presentation: () => i18n.translate('xpack.canvas.tags.presentationTag', { defaultMessage: 'presentation', }), - proportion: () => - i18n.translate('xpack.canvas.tags.proportionTag', { - defaultMessage: 'proportion', - }), report: () => i18n.translate('xpack.canvas.tags.reportTag', { defaultMessage: 'report', }), - text: () => - i18n.translate('xpack.canvas.tags.textTag', { - defaultMessage: 'text', - }), }; diff --git a/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.scss b/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.scss index c5161439b71c3..c7dae8452a93c 100644 --- a/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.scss +++ b/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.scss @@ -31,7 +31,7 @@ $canvasLayoutFontSize: $euiFontSizeS; .canvasLayout__stageHeader { flex-grow: 0; flex-basis: auto; - padding: ($euiSizeXS + 1px) $euiSize $euiSizeXS; + padding: 1px $euiSize 0; font-size: $canvasLayoutFontSize; border-bottom: $euiBorderThin; background: $euiColorLightestShade; diff --git a/x-pack/legacy/plugins/canvas/public/components/asset_manager/__examples__/__snapshots__/asset_manager.stories.storyshot b/x-pack/legacy/plugins/canvas/public/components/asset_manager/__examples__/__snapshots__/asset_manager.stories.storyshot new file mode 100644 index 0000000000000..aff630b21c770 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/asset_manager/__examples__/__snapshots__/asset_manager.stories.storyshot @@ -0,0 +1,761 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Storyshots components/Assets/AssetManager no assets 1`] = ` +Array [ +

, +
, +
+
+ +
+
+
+ Manage workpad assets +
+
+
+
+
+ +
+ +
+
+
+
+
+
+
+
+
+

+ Below are the image assets in this workpad. Any assets that are currently in use cannot be determined at this time. To reclaim space, delete assets. +

+
+
+
+
+
+
+
+ +

+ Import your assets to get started +

+
+ +
+
+
+
+
+
+
+ +
+
+
+ 0% space used +
+
+
+ +
+
+
+
, +
, +] +`; + +exports[`Storyshots components/Assets/AssetManager two assets 1`] = ` +Array [ +
, +
, +
+
+ +
+
+
+ Manage workpad assets +
+
+
+
+
+ +
+ +
+
+
+
+
+
+
+
+
+

+ Below are the image assets in this workpad. Any assets that are currently in use cannot be determined at this time. To reclaim space, delete assets. +

+
+
+
+
+
+
+
+
+ Asset thumbnail +
+
+
+
+

+ + airplane + +
+ + + ( + 1 + kb) + + +

+
+
+
+
+ + + +
+
+ +
+ +
+
+
+
+ +
+ +
+
+
+
+ + + +
+
+
+
+
+
+
+
+ Asset thumbnail +
+
+
+
+

+ + marker + +
+ + + ( + 1 + kb) + + +

+
+
+
+
+ + + +
+
+ +
+ +
+
+
+
+ +
+ +
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+ +
+
+
+ 0% space used +
+
+
+ +
+
+
+
, +
, +] +`; diff --git a/x-pack/legacy/plugins/canvas/public/components/asset_manager/__examples__/asset_manager.examples.tsx b/x-pack/legacy/plugins/canvas/public/components/asset_manager/__examples__/asset_manager.stories.tsx similarity index 97% rename from x-pack/legacy/plugins/canvas/public/components/asset_manager/__examples__/asset_manager.examples.tsx rename to x-pack/legacy/plugins/canvas/public/components/asset_manager/__examples__/asset_manager.stories.tsx index 045a3b4f64a44..cb42823ccab7b 100644 --- a/x-pack/legacy/plugins/canvas/public/components/asset_manager/__examples__/asset_manager.examples.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/asset_manager/__examples__/asset_manager.stories.tsx @@ -28,12 +28,12 @@ const MARKER: AssetType = { storiesOf('components/Assets/AssetManager', module) .add('no assets', () => ( - // @ts-ignore @types/react has not been updated to support defaultProps yet. )) .add('two assets', () => ( @@ -43,5 +43,6 @@ storiesOf('components/Assets/AssetManager', module) onAssetAdd={action('onAssetAdd')} onAssetCopy={action('onAssetCopy')} onAssetDelete={action('onAssetDelete')} + onClose={action('onClose')} /> )); diff --git a/x-pack/legacy/plugins/canvas/public/components/asset_manager/asset_manager.tsx b/x-pack/legacy/plugins/canvas/public/components/asset_manager/asset_manager.tsx index 3785f81cc25b9..479e9287d7adf 100644 --- a/x-pack/legacy/plugins/canvas/public/components/asset_manager/asset_manager.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/asset_manager/asset_manager.tsx @@ -4,13 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - EuiButtonEmpty, - // @ts-ignore (elastic/eui#1557) EuiFilePicker is not exported yet - EuiFilePicker, - // @ts-ignore (elastic/eui#1557) EuiImage is not exported yet - EuiImage, -} from '@elastic/eui'; import PropTypes from 'prop-types'; import React, { Fragment, PureComponent } from 'react'; @@ -33,13 +26,13 @@ interface Props { onAssetCopy: () => void; /** Function to invoke when an asset is added */ onAssetAdd: (asset: File) => void; + /** Function to invoke when an asset modal is closed */ + onClose: () => void; } interface State { /** The id of the asset to delete, if applicable. Is set if the viewer clicks the delete icon */ deleteId: string | null; - /** Determines if the modal is currently visible */ - isModalVisible: boolean; /** Indicates if the modal is currently loading */ isLoading: boolean; } @@ -51,6 +44,7 @@ export class AssetManager extends PureComponent { onAssetAdd: PropTypes.func.isRequired, onAssetCopy: PropTypes.func.isRequired, onAssetDelete: PropTypes.func.isRequired, + onClose: PropTypes.func.isRequired, }; public static defaultProps = { @@ -60,12 +54,11 @@ export class AssetManager extends PureComponent { public state = { deleteId: null, isLoading: false, - isModalVisible: false, }; public render() { - const { isModalVisible, isLoading } = this.state; - const { assetValues, onAssetCopy, onAddImageElement } = this.props; + const { isLoading } = this.state; + const { assetValues, onAssetCopy, onAddImageElement, onClose } = this.props; const assetModal = ( { onAssetCopy={onAssetCopy} onAssetCreate={(createdAsset: AssetType) => { onAddImageElement(createdAsset.id); - this.setState({ isModalVisible: false }); + onClose(); }} onAssetDelete={(asset: AssetType) => this.setState({ deleteId: asset.id })} - onClose={() => this.setState({ isModalVisible: false })} + onClose={onClose} onFileUpload={this.handleFileUpload} /> ); @@ -95,14 +88,12 @@ export class AssetManager extends PureComponent { return ( - {strings.getButtonLabel()} - {isModalVisible ? assetModal : null} + {assetModal} {confirmModal} ); } - private showModal = () => this.setState({ isModalVisible: true }); private resetDelete = () => this.setState({ deleteId: null }); private doDelete = () => { diff --git a/x-pack/legacy/plugins/canvas/public/components/asset_manager/asset_modal.tsx b/x-pack/legacy/plugins/canvas/public/components/asset_manager/asset_modal.tsx index 3dfbb1b1fde3c..8637db8e9f962 100644 --- a/x-pack/legacy/plugins/canvas/public/components/asset_manager/asset_modal.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/asset_manager/asset_modal.tsx @@ -98,6 +98,7 @@ export const AssetModal: FunctionComponent = props => { -
-
-
-
- -
-
- - - -
-

- sample description -

-
-
-
-
-
-
- - - -
-
- - - -
-
-
-
-
-
- -
-
- - - -
-

- Aenean eu justo auctor, placerat felis non, scelerisque dolor. -

-
-
-
-
-
-
- - - -
-
- - - -
-
-
-
-
-
- -
-
- - - -
-

- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce lobortis aliquet arcu ut turpis duis. -

-
-
-
-
-
-
- - - -
-
- - - -
-
-
-
-
-`; - -exports[`Storyshots components/Elements/ElementGrid with controls and filter 1`] = ` -
-
-
-
-
- -
-
- - - -
-

- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce lobortis aliquet arcu ut turpis duis. -

-
-
-
-
-
-
- - - -
-
- - - -
-
-
-
-
-`; - -exports[`Storyshots components/Elements/ElementGrid with tags filter 1`] = ` -
-
-
-
-
- -
-
- - - -
-

- A static image -

-
-
-
- - - - graphic - - - -
-
-
-
-
-`; - -exports[`Storyshots components/Elements/ElementGrid with text filter 1`] = ` -
-
-
-
-
- -
-
- - - -
-

- A scrollable grid for displaying data in a tabular format -

-
-
-
- - - - text - - - -
-
-
-
-
-`; - -exports[`Storyshots components/Elements/ElementGrid without controls 1`] = ` -
-
-
-
-
- -
-
- - - -
-

- A line chart with a filled body -

-
-
-
- - - - chart - - - -
-
-
-
-
-
- -
-
- - - -
-

- A static image -

-
-
-
- - - - graphic - - - -
-
-
-
-
-
- -
-
- - - -
-

- A scrollable grid for displaying data in a tabular format -

-
-
-
- - - - text - - - -
-
-
-
-
-`; diff --git a/x-pack/legacy/plugins/canvas/public/components/element_types/element_grid.tsx b/x-pack/legacy/plugins/canvas/public/components/element_types/element_grid.tsx deleted file mode 100644 index 852b987fcfd24..0000000000000 --- a/x-pack/legacy/plugins/canvas/public/components/element_types/element_grid.tsx +++ /dev/null @@ -1,110 +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 from 'react'; -import PropTypes from 'prop-types'; -import { map } from 'lodash'; -import { EuiFlexItem, EuiFlexGrid } from '@elastic/eui'; -import { ElementControls } from './element_controls'; -import { CustomElement, ElementSpec } from '../../../types'; -import { ElementCard } from '../element_card'; - -export interface Props { - /** - * list of elements to generate cards for - */ - elements: Array; - /** - * text to filter out cards - */ - filterText: string; - /** - * tags to filter out cards - */ - filterTags: string[]; - /** - * indicate whether or not edit/delete controls should be displayed - */ - showControls: boolean; - /** - * handler invoked when clicking a card - */ - handleClick: (element: ElementSpec | CustomElement) => void; - /** - * click handler for the edit button - */ - onEdit?: (element: ElementSpec | CustomElement) => void; - /** - * click handler for the delete button - */ - onDelete?: (element: ElementSpec | CustomElement) => void; -} - -export const ElementGrid = ({ - elements, - filterText, - filterTags, - handleClick, - onEdit, - onDelete, - showControls, -}: Props) => { - filterText = filterText.toLowerCase(); - - return ( - - {map(elements, (element: ElementSpec | CustomElement, index) => { - const { name, displayName = '', help = '', image, tags = [] } = element; - const whenClicked = () => handleClick(element); - let textMatch = false; - let tagsMatch = false; - - if ( - !filterText.length || - name.toLowerCase().includes(filterText) || - displayName.toLowerCase().includes(filterText) || - help.toLowerCase().includes(filterText) - ) { - textMatch = true; - } - - if (!filterTags.length || filterTags.every(tag => tags.includes(tag))) { - tagsMatch = true; - } - - if (!textMatch || !tagsMatch) { - return null; - } - - return ( - - - {showControls && onEdit && onDelete && ( - onEdit(element)} onDelete={() => onDelete(element)} /> - )} - - ); - })} - - ); -}; - -ElementGrid.propTypes = { - elements: PropTypes.array.isRequired, - handleClick: PropTypes.func.isRequired, - showControls: PropTypes.bool, -}; - -ElementGrid.defaultProps = { - showControls: false, - filterTags: [], - filterText: '', -}; diff --git a/x-pack/legacy/plugins/canvas/public/components/element_types/element_types.js b/x-pack/legacy/plugins/canvas/public/components/element_types/element_types.js deleted file mode 100644 index dabf06a24aeb6..0000000000000 --- a/x-pack/legacy/plugins/canvas/public/components/element_types/element_types.js +++ /dev/null @@ -1,224 +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, { Component, Fragment } from 'react'; -import PropTypes from 'prop-types'; -import { - EuiModalBody, - EuiTabbedContent, - EuiEmptyPrompt, - EuiSearchBar, - EuiSpacer, - EuiOverlayMask, -} from '@elastic/eui'; -import { map, sortBy } from 'lodash'; -import { ConfirmModal } from '../confirm_modal/confirm_modal'; -import { CustomElementModal } from '../custom_element_modal'; -import { getTagsFilter } from '../../lib/get_tags_filter'; -import { extractSearch } from '../../lib/extract_search'; -import { ComponentStrings } from '../../../i18n'; -import { ElementGrid } from './element_grid'; - -const { ElementTypes: strings } = ComponentStrings; - -const tagType = 'badge'; -export class ElementTypes extends Component { - static propTypes = { - addCustomElement: PropTypes.func.isRequired, - addElement: PropTypes.func.isRequired, - customElements: PropTypes.array.isRequired, - elements: PropTypes.object, - filterTags: PropTypes.arrayOf(PropTypes.string).isRequired, - findCustomElements: PropTypes.func.isRequired, - onClose: PropTypes.func.isRequired, - removeCustomElement: PropTypes.func.isRequired, - search: PropTypes.string, - setCustomElements: PropTypes.func.isRequired, - setSearch: PropTypes.func.isRequired, - setFilterTags: PropTypes.func.isRequired, - updateCustomElement: PropTypes.func.isRequired, - }; - - state = { - elementToDelete: null, - elementToEdit: null, - }; - - componentDidMount() { - // fetch custom elements - this.props.findCustomElements(); - } - - _showEditModal = elementToEdit => this.setState({ elementToEdit }); - - _hideEditModal = () => this.setState({ elementToEdit: null }); - - _handleEdit = async (name, description, image) => { - const { updateCustomElement } = this.props; - const { elementToEdit } = this.state; - await updateCustomElement(elementToEdit.id, name, description, image); - this._hideEditModal(); - }; - - _showDeleteModal = elementToDelete => this.setState({ elementToDelete }); - - _hideDeleteModal = () => this.setState({ elementToDelete: null }); - - _handleDelete = async () => { - const { removeCustomElement } = this.props; - const { elementToDelete } = this.state; - await removeCustomElement(elementToDelete.id); - this._hideDeleteModal(); - }; - - _renderEditModal = () => { - const { elementToEdit } = this.state; - - if (!elementToEdit) { - return null; - } - - return ( - - - - ); - }; - - _renderDeleteModal = () => { - const { elementToDelete } = this.state; - - if (!elementToDelete) { - return null; - } - - return ( - - ); - }; - - _sortElements = elements => - sortBy( - map(elements, (element, name) => ({ name, ...element })), - 'displayName' - ); - - render() { - const { - search, - setSearch, - addElement, - addCustomElement, - filterTags, - setFilterTags, - } = this.props; - let { elements, customElements } = this.props; - elements = this._sortElements(elements); - - let customElementContent = ( - {strings.getAddNewElementTitle()}} - body={

{strings.getAddNewElementDescription()}

} - titleSize="s" - /> - ); - - if (customElements.length) { - customElements = this._sortElements(customElements); - customElementContent = ( - - ); - } - - const filters = [getTagsFilter(tagType)]; - const onSearch = ({ queryText }) => { - const { searchTerm, filterTags } = extractSearch(queryText); - setSearch(searchTerm); - setFilterTags(filterTags); - }; - - const tabs = [ - { - id: 'elements', - name: strings.getElementsTitle(), - content: ( -
- - - - -
- ), - }, - { - id: 'customElements', - name: strings.getMyElementsTitle(), - content: ( - - - - - {customElementContent} - - ), - }, - ]; - - return ( - - - - - - {this._renderDeleteModal()} - {this._renderEditModal()} - - ); - } -} diff --git a/x-pack/legacy/plugins/canvas/public/components/element_types/index.js b/x-pack/legacy/plugins/canvas/public/components/element_types/index.js deleted file mode 100644 index 8faaf278a07de..0000000000000 --- a/x-pack/legacy/plugins/canvas/public/components/element_types/index.js +++ /dev/null @@ -1,103 +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 PropTypes from 'prop-types'; -import { compose, withProps, withState } from 'recompose'; -import { connect } from 'react-redux'; -import { camelCase } from 'lodash'; -import { cloneSubgraphs } from '../../lib/clone_subgraphs'; -import * as customElementService from '../../lib/custom_element_service'; -import { elementsRegistry } from '../../lib/elements_registry'; -import { notify } from '../../lib/notify'; -import { selectToplevelNodes } from '../../state/actions/transient'; -import { insertNodes, addElement } from '../../state/actions/elements'; -import { getSelectedPage } from '../../state/selectors/workpad'; -import { trackCanvasUiMetric, METRIC_TYPE } from '../../lib/ui_metric'; -import { ElementTypes as Component } from './element_types'; - -const customElementAdded = 'elements-custom-added'; - -const mapStateToProps = state => ({ pageId: getSelectedPage(state) }); - -const mapDispatchToProps = dispatch => ({ - selectToplevelNodes: nodes => - dispatch(selectToplevelNodes(nodes.filter(e => !e.position.parent).map(e => e.id))), - insertNodes: (selectedNodes, pageId) => dispatch(insertNodes(selectedNodes, pageId)), - addElement: (pageId, partialElement) => dispatch(addElement(pageId, partialElement)), -}); - -const mergeProps = (stateProps, dispatchProps, ownProps) => { - const { pageId, ...remainingStateProps } = stateProps; - const { addElement, insertNodes, selectToplevelNodes } = dispatchProps; - const { search, setCustomElements, onClose } = ownProps; - - return { - ...remainingStateProps, - ...ownProps, - // add built-in element to the page - addElement: element => { - addElement(pageId, element); - onClose(); - }, - // add custom element to the page - addCustomElement: customElement => { - const { selectedNodes = [] } = JSON.parse(customElement.content) || {}; - const clonedNodes = selectedNodes && cloneSubgraphs(selectedNodes); - if (clonedNodes) { - insertNodes(clonedNodes, pageId); // first clone and persist the new node(s) - selectToplevelNodes(clonedNodes); // then select the cloned node(s) - } - onClose(); - trackCanvasUiMetric(METRIC_TYPE.LOADED, customElementAdded); - }, - // custom element search - findCustomElements: async text => { - try { - const { customElements } = await customElementService.find(text); - setCustomElements(customElements); - } catch (err) { - notify.error(err, { title: `Couldn't find custom elements` }); - } - }, - // remove custom element - removeCustomElement: async id => { - try { - await customElementService.remove(id).then(); - const { customElements } = await customElementService.find(search); - setCustomElements(customElements); - } catch (err) { - notify.error(err, { title: `Couldn't delete custom elements` }); - } - }, - // update custom element - updateCustomElement: async (id, name, description, image) => { - try { - await customElementService.update(id, { - name: camelCase(name), - displayName: name, - image, - help: description, - }); - const { customElements } = await customElementService.find(search); - setCustomElements(customElements); - } catch (err) { - notify.error(err, { title: `Couldn't update custom elements` }); - } - }, - }; -}; - -export const ElementTypes = compose( - withState('search', 'setSearch', ''), - withState('customElements', 'setCustomElements', []), - withState('filterTags', 'setFilterTags', []), - withProps(() => ({ elements: elementsRegistry.toJS() })), - connect(mapStateToProps, mapDispatchToProps, mergeProps) -)(Component); - -ElementTypes.propTypes = { - onClose: PropTypes.func, -}; diff --git a/x-pack/legacy/plugins/canvas/public/components/popover/index.ts b/x-pack/legacy/plugins/canvas/public/components/popover/index.ts index f560da14079b5..63626f08fa43b 100644 --- a/x-pack/legacy/plugins/canvas/public/components/popover/index.ts +++ b/x-pack/legacy/plugins/canvas/public/components/popover/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { Popover } from './popover'; +export { Popover, ClosePopoverFn } from './popover'; diff --git a/x-pack/legacy/plugins/canvas/public/components/popover/popover.tsx b/x-pack/legacy/plugins/canvas/public/components/popover/popover.tsx index 25b2e6587c869..9f3d86576e6a7 100644 --- a/x-pack/legacy/plugins/canvas/public/components/popover/popover.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/popover/popover.tsx @@ -24,6 +24,8 @@ interface Props { className?: string; } +export type ClosePopoverFn = () => void; + interface State { isPopoverOpen: boolean; } @@ -61,7 +63,7 @@ export class Popover extends Component { })); }; - closePopover = () => { + closePopover: ClosePopoverFn = () => { this.setState({ isPopoverOpen: false, }); diff --git a/x-pack/legacy/plugins/canvas/public/components/element_types/__examples__/__snapshots__/element_controls.stories.storyshot b/x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/__examples__/__snapshots__/element_controls.stories.storyshot similarity index 88% rename from x-pack/legacy/plugins/canvas/public/components/element_types/__examples__/__snapshots__/element_controls.stories.storyshot rename to x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/__examples__/__snapshots__/element_controls.stories.storyshot index 5ce6fe8c85589..6f12f68356467 100644 --- a/x-pack/legacy/plugins/canvas/public/components/element_types/__examples__/__snapshots__/element_controls.stories.storyshot +++ b/x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/__examples__/__snapshots__/element_controls.stories.storyshot @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Storyshots components/Elements/ElementControls has two buttons 1`] = ` +exports[`Storyshots components/SavedElementsModal/ElementControls has two buttons 1`] = `
+
+
+
+
+ +
+
+ + + +
+

+ sample description +

+
+
+
+
+
+
+ + + +
+
+ + + +
+
+
+
+
+
+ +
+
+ + + +
+

+ Aenean eu justo auctor, placerat felis non, scelerisque dolor. +

+
+
+
+
+
+
+ + + +
+
+ + + +
+
+
+
+
+
+ +
+
+ + + +
+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce lobortis aliquet arcu ut turpis duis. +

+
+
+
+
+
+
+ + + +
+
+ + + +
+
+
+
+
+`; + +exports[`Storyshots components/SavedElementsModal/ElementGrid with text filter 1`] = ` +
+
+
+`; diff --git a/x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/__examples__/__snapshots__/saved_elements_modal.stories.storyshot b/x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/__examples__/__snapshots__/saved_elements_modal.stories.storyshot new file mode 100644 index 0000000000000..c73309ad8b14c --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/__examples__/__snapshots__/saved_elements_modal.stories.storyshot @@ -0,0 +1,933 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Storyshots components/SavedElementsModal no custom elements 1`] = ` +Array [ +
, +
, +
+
+ +
+
+
+ My elements +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+ +

+ Add new elements +

+
+
+

+ Group and save workpad elements to create new elements +

+
+ +
+
+
+
+ +
+
+
+
, +
, +] +`; + +exports[`Storyshots components/SavedElementsModal with custom elements 1`] = ` +Array [ +
, +
, +
+
+ +
+
+
+ My elements +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+ +
+
+ + + +
+

+ sample description +

+
+
+
+
+
+
+ + + +
+
+ + + +
+
+
+
+
+
+ +
+
+ + + +
+

+ Aenean eu justo auctor, placerat felis non, scelerisque dolor. +

+
+
+
+
+
+
+ + + +
+
+ + + +
+
+
+
+
+
+ +
+
+ + + +
+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce lobortis aliquet arcu ut turpis duis. +

+
+
+
+
+
+
+ + + +
+
+ + + +
+
+
+
+
+
+
+ +
+
+
+
, +
, +] +`; + +exports[`Storyshots components/SavedElementsModal with text filter 1`] = ` +Array [ +
, +
, +
+
+ +
+
+
+ My elements +
+
+
+
+
+
+ +
+ + +
+ +
+
+
+
+
+
+
+
+ +
+
+ + + +
+

+ Aenean eu justo auctor, placerat felis non, scelerisque dolor. +

+
+
+
+
+
+
+ + + +
+
+ + + +
+
+
+
+
+
+
+ +
+
+
+
, +
, +] +`; diff --git a/x-pack/legacy/plugins/canvas/public/components/element_types/__examples__/element_controls.stories.tsx b/x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/__examples__/element_controls.stories.tsx similarity index 90% rename from x-pack/legacy/plugins/canvas/public/components/element_types/__examples__/element_controls.stories.tsx rename to x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/__examples__/element_controls.stories.tsx index 52736ac952e53..5210210ebaa74 100644 --- a/x-pack/legacy/plugins/canvas/public/components/element_types/__examples__/element_controls.stories.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/__examples__/element_controls.stories.tsx @@ -9,7 +9,7 @@ import { storiesOf } from '@storybook/react'; import { action } from '@storybook/addon-actions'; import { ElementControls } from '../element_controls'; -storiesOf('components/Elements/ElementControls', module) +storiesOf('components/SavedElementsModal/ElementControls', module) .addDecorator(story => (
(
)) - .add('without controls', () => ( - - )) - .add('with controls', () => ( + .add('default', () => ( )) .add('with text filter', () => ( - - )) - .add('with tags filter', () => ( - - )) - .add('with controls and filter', () => ( diff --git a/x-pack/legacy/plugins/canvas/public/components/element_types/__examples__/fixtures/test_elements.tsx b/x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/__examples__/fixtures/test_elements.tsx similarity index 93% rename from x-pack/legacy/plugins/canvas/public/components/element_types/__examples__/fixtures/test_elements.tsx rename to x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/__examples__/fixtures/test_elements.tsx index eec7a86d52f25..d1ff565b4955a 100644 --- a/x-pack/legacy/plugins/canvas/public/components/element_types/__examples__/fixtures/test_elements.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/__examples__/fixtures/test_elements.tsx @@ -6,41 +6,6 @@ import { elasticLogo } from '../../../../lib/elastic_logo'; -export const testElements = [ - { - name: 'areaChart', - displayName: 'Area chart', - help: 'A line chart with a filled body', - tags: ['chart'], - image: elasticLogo, - expression: `filters - | demodata - | pointseries x="time" y="mean(price)" - | plot defaultStyle={seriesStyle lines=1 fill=1} - | render`, - }, - { - name: 'image', - displayName: 'Image', - help: 'A static image', - tags: ['graphic'], - image: elasticLogo, - expression: `image dataurl=null mode="contain" - | render`, - }, - { - name: 'table', - displayName: 'Data table', - tags: ['text'], - help: 'A scrollable grid for displaying data in a tabular format', - image: elasticLogo, - expression: `filters - | demodata - | table - | render`, - }, -]; - export const testCustomElements = [ { id: 'custom-element-10d625f5-1342-47c9-8f19-d174ea6b65d5', diff --git a/x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/__examples__/saved_elements_modal.stories.tsx b/x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/__examples__/saved_elements_modal.stories.tsx new file mode 100644 index 0000000000000..4941d8cb2efa7 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/__examples__/saved_elements_modal.stories.tsx @@ -0,0 +1,50 @@ +/* + * 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 { storiesOf } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; +import { SavedElementsModal } from '../saved_elements_modal'; +import { testCustomElements } from './fixtures/test_elements'; +import { CustomElement } from '../../../../types'; + +storiesOf('components/SavedElementsModal', module) + .add('no custom elements', () => ( + + )) + .add('with custom elements', () => ( + + )) + .add('with text filter', () => ( + + )); diff --git a/x-pack/legacy/plugins/canvas/public/components/element_types/element_controls.tsx b/x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/element_controls.tsx similarity index 85% rename from x-pack/legacy/plugins/canvas/public/components/element_types/element_controls.tsx rename to x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/element_controls.tsx index a23274296f64f..998b15c15f487 100644 --- a/x-pack/legacy/plugins/canvas/public/components/element_types/element_controls.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/element_controls.tsx @@ -30,7 +30,12 @@ export const ElementControls: FunctionComponent = ({ onDelete, onEdit }) > - + @@ -40,6 +45,7 @@ export const ElementControls: FunctionComponent = ({ onDelete, onEdit }) iconType="trash" aria-label={strings.getDeleteAriaLabel()} onClick={onDelete} + data-test-subj="canvasElementCard__deleteButton" /> diff --git a/x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/element_grid.tsx b/x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/element_grid.tsx new file mode 100644 index 0000000000000..f86e2c0147035 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/element_grid.tsx @@ -0,0 +1,81 @@ +/* + * 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 PropTypes from 'prop-types'; +import { map } from 'lodash'; +import { EuiFlexItem, EuiFlexGrid } from '@elastic/eui'; +import { ElementControls } from './element_controls'; +import { CustomElement } from '../../../types'; +import { ElementCard } from '../element_card'; + +export interface Props { + /** + * list of elements to generate cards for + */ + elements: CustomElement[]; + /** + * text to filter out cards + */ + filterText: string; + /** + * handler invoked when clicking a card + */ + onClick: (element: CustomElement) => void; + /** + * click handler for the edit button + */ + onEdit: (element: CustomElement) => void; + /** + * click handler for the delete button + */ + onDelete: (element: CustomElement) => void; +} + +export const ElementGrid = ({ elements, filterText, onClick, onEdit, onDelete }: Props) => { + filterText = filterText.toLowerCase(); + + return ( + + {map(elements, (element: CustomElement, index) => { + const { name, displayName = '', help = '', image } = element; + const whenClicked = () => onClick(element); + + if ( + filterText.length && + !name.toLowerCase().includes(filterText) && + !displayName.toLowerCase().includes(filterText) && + !help.toLowerCase().includes(filterText) + ) { + return null; + } + + return ( + + + onEdit(element)} onDelete={() => onDelete(element)} /> + + ); + })} + + ); +}; + +ElementGrid.propTypes = { + elements: PropTypes.array.isRequired, + filterText: PropTypes.string.isRequired, + onClick: PropTypes.func.isRequired, + onEdit: PropTypes.func.isRequired, + onDelete: PropTypes.func.isRequired, +}; + +ElementGrid.defaultProps = { + filterText: '', +}; diff --git a/x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/index.ts b/x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/index.ts new file mode 100644 index 0000000000000..bb088ad4e0de1 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/index.ts @@ -0,0 +1,128 @@ +/* + * 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 { connect } from 'react-redux'; +import { Dispatch } from 'redux'; +import { compose, withState } from 'recompose'; +import { camelCase } from 'lodash'; +// @ts-ignore Untyped local +import { cloneSubgraphs } from '../../lib/clone_subgraphs'; +import * as customElementService from '../../lib/custom_element_service'; +// @ts-ignore Untyped local +import { notify } from '../../lib/notify'; +// @ts-ignore Untyped local +import { selectToplevelNodes } from '../../state/actions/transient'; +// @ts-ignore Untyped local +import { insertNodes } from '../../state/actions/elements'; +import { getSelectedPage } from '../../state/selectors/workpad'; +import { trackCanvasUiMetric, METRIC_TYPE } from '../../lib/ui_metric'; +import { SavedElementsModal as Component, Props as ComponentProps } from './saved_elements_modal'; +import { State, PositionedElement, CustomElement } from '../../../types'; + +const customElementAdded = 'elements-custom-added'; + +interface OwnProps { + onClose: () => void; +} + +interface OwnPropsWithState extends OwnProps { + customElements: CustomElement[]; + setCustomElements: (customElements: CustomElement[]) => void; + search: string; + setSearch: (search: string) => void; +} + +interface DispatchProps { + selectToplevelNodes: (nodes: PositionedElement[]) => void; + insertNodes: (selectedNodes: PositionedElement[], pageId: string) => void; +} + +interface StateProps { + pageId: string; +} + +const mapStateToProps = (state: State): StateProps => ({ + pageId: getSelectedPage(state), +}); + +const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({ + selectToplevelNodes: (nodes: PositionedElement[]) => + dispatch( + selectToplevelNodes( + nodes + .filter((e: PositionedElement): boolean => !e.position.parent) + .map((e: PositionedElement): string => e.id) + ) + ), + insertNodes: (selectedNodes: PositionedElement[], pageId: string) => + dispatch(insertNodes(selectedNodes, pageId)), +}); + +const mergeProps = ( + stateProps: StateProps, + dispatchProps: DispatchProps, + ownProps: OwnPropsWithState +): ComponentProps => { + const { pageId } = stateProps; + const { onClose, search, setCustomElements } = ownProps; + + const findCustomElements = async () => { + const { customElements } = await customElementService.find(search); + setCustomElements(customElements); + }; + + return { + ...ownProps, + // add custom element to the page + addCustomElement: (customElement: CustomElement) => { + const { selectedNodes = [] } = JSON.parse(customElement.content) || {}; + const clonedNodes = selectedNodes && cloneSubgraphs(selectedNodes); + if (clonedNodes) { + dispatchProps.insertNodes(clonedNodes, pageId); // first clone and persist the new node(s) + dispatchProps.selectToplevelNodes(clonedNodes); // then select the cloned node(s) + } + onClose(); + trackCanvasUiMetric(METRIC_TYPE.LOADED, customElementAdded); + }, + // custom element search + findCustomElements: async (text?: string) => { + try { + await findCustomElements(); + } catch (err) { + notify.error(err, { title: `Couldn't find custom elements` }); + } + }, + // remove custom element + removeCustomElement: async (id: string) => { + try { + await customElementService.remove(id); + await findCustomElements(); + } catch (err) { + notify.error(err, { title: `Couldn't delete custom elements` }); + } + }, + // update custom element + updateCustomElement: async (id: string, name: string, description: string, image: string) => { + try { + await customElementService.update(id, { + name: camelCase(name), + displayName: name, + image, + help: description, + }); + await findCustomElements(); + } catch (err) { + notify.error(err, { title: `Couldn't update custom elements` }); + } + }, + }; +}; + +export const SavedElementsModal = compose( + withState('search', 'setSearch', ''), + withState('customElements', 'setCustomElements', []), + connect(mapStateToProps, mapDispatchToProps, mergeProps) +)(Component); diff --git a/x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/saved_elements_modal.tsx b/x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/saved_elements_modal.tsx new file mode 100644 index 0000000000000..dba97a15fee5c --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/saved_elements_modal/saved_elements_modal.tsx @@ -0,0 +1,217 @@ +/* + * 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, { Fragment, ChangeEvent, FunctionComponent, useState, useEffect } from 'react'; +import PropTypes from 'prop-types'; +import { + EuiModal, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, + EuiEmptyPrompt, + EuiFieldSearch, + EuiSpacer, + EuiOverlayMask, + EuiButton, +} from '@elastic/eui'; +import { map, sortBy } from 'lodash'; +import { ComponentStrings } from '../../../i18n'; +import { CustomElement } from '../../../types'; +import { ConfirmModal } from '../confirm_modal/confirm_modal'; +import { CustomElementModal } from '../custom_element_modal'; +import { ElementGrid } from './element_grid'; + +const { SavedElementsModal: strings } = ComponentStrings; + +export interface Props { + /** + * Adds the custom element to the workpad + */ + addCustomElement: (customElement: CustomElement) => void; + /** + * Queries ES for custom element saved objects + */ + findCustomElements: () => void; + /** + * Handler invoked when the modal closes + */ + onClose: () => void; + /** + * Deletes the custom element + */ + removeCustomElement: (id: string) => void; + /** + * Saved edits to the custom element + */ + updateCustomElement: (id: string, name: string, description: string, image: string) => void; + /** + * Array of custom elements to display + */ + customElements: CustomElement[]; + /** + * Text used to filter custom elements list + */ + search: string; + /** + * Setter for search text + */ + setSearch: (search: string) => void; +} + +export const SavedElementsModal: FunctionComponent = ({ + search, + setSearch, + customElements, + addCustomElement, + findCustomElements, + onClose, + removeCustomElement, + updateCustomElement, +}) => { + const [elementToDelete, setElementToDelete] = useState(null); + const [elementToEdit, setElementToEdit] = useState(null); + + useEffect(() => { + findCustomElements(); + }); + + const showEditModal = (element: CustomElement) => setElementToEdit(element); + const hideEditModal = () => setElementToEdit(null); + + const handleEdit = async (name: string, description: string, image: string) => { + if (elementToEdit) { + await updateCustomElement(elementToEdit.id, name, description, image); + } + hideEditModal(); + }; + + const showDeleteModal = (element: CustomElement) => setElementToDelete(element); + const hideDeleteModal = () => setElementToDelete(null); + + const handleDelete = async () => { + if (elementToDelete) { + await removeCustomElement(elementToDelete.id); + } + hideDeleteModal(); + }; + + const renderEditModal = () => { + if (!elementToEdit) { + return null; + } + + return ( + + + + ); + }; + + const renderDeleteModal = () => { + if (!elementToDelete) { + return null; + } + + return ( + + ); + }; + + const sortElements = (elements: CustomElement[]): CustomElement[] => + sortBy( + map(elements, (element, name) => ({ name, ...element })), + 'displayName' + ); + + const onSearch = (e: ChangeEvent) => setSearch(e.target.value); + + let customElementContent = ( + {strings.getAddNewElementTitle()}} + body={

{strings.getAddNewElementDescription()}

} + titleSize="s" + /> + ); + + if (customElements.length) { + customElementContent = ( + + ); + } + + return ( + + + + + + {strings.getModalTitle()} + + + + + + + {customElementContent} + + + + {strings.getSavedElementsModalCloseButtonLabel()} + + + + + + {renderDeleteModal()} + {renderEditModal()} + + ); +}; + +SavedElementsModal.propTypes = { + addCustomElement: PropTypes.func.isRequired, + findCustomElements: PropTypes.func.isRequired, + onClose: PropTypes.func.isRequired, + removeCustomElement: PropTypes.func.isRequired, + updateCustomElement: PropTypes.func.isRequired, +}; diff --git a/x-pack/legacy/plugins/canvas/public/components/sidebar_header/__examples__/__snapshots__/sidebar_header.stories.storyshot b/x-pack/legacy/plugins/canvas/public/components/sidebar_header/__examples__/__snapshots__/sidebar_header.stories.storyshot index d46a509251d35..ac25cbe0b6832 100644 --- a/x-pack/legacy/plugins/canvas/public/components/sidebar_header/__examples__/__snapshots__/sidebar_header.stories.storyshot +++ b/x-pack/legacy/plugins/canvas/public/components/sidebar_header/__examples__/__snapshots__/sidebar_header.stories.storyshot @@ -37,6 +37,7 @@ exports[`Storyshots components/Sidebar/SidebarHeader default 1`] = ` + +
+
+ + + +
+
+ + + +
+
+ + + +
@@ -134,6 +235,7 @@ exports[`Storyshots components/Sidebar/SidebarHeader without layer controls 1`] +
+
+`; diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/element_menu/__examples__/element_menu.examples.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/element_menu/__examples__/element_menu.examples.tsx new file mode 100644 index 0000000000000..9aca5ce33ba02 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/element_menu/__examples__/element_menu.examples.tsx @@ -0,0 +1,150 @@ +/* + * 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 { storiesOf } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; +import React from 'react'; +import { ElementSpec } from '../../../../../types'; +import { ElementMenu } from '../element_menu'; + +const testElements: { [key: string]: ElementSpec } = { + areaChart: { + name: 'areaChart', + displayName: 'Area chart', + help: 'A line chart with a filled body', + type: 'chart', + expression: `filters + | demodata + | pointseries x="time" y="mean(price)" + | plot defaultStyle={seriesStyle lines=1 fill=1} + | render`, + }, + debug: { + name: 'debug', + displayName: 'Debug data', + help: 'Just dumps the configuration of the element', + icon: 'bug', + expression: `demodata + | render as=debug`, + }, + dropdownFilter: { + name: 'dropdownFilter', + displayName: 'Dropdown select', + type: 'filter', + help: 'A dropdown from which you can select values for an "exactly" filter', + icon: 'filter', + height: 50, + expression: `demodata + | dropdownControl valueColumn=project filterColumn=project | render`, + filter: '', + }, + filterDebug: { + name: 'filterDebug', + displayName: 'Debug filter', + help: 'Shows the underlying global filters in a workpad', + icon: 'bug', + expression: `filters + | render as=debug`, + }, + image: { + name: 'image', + displayName: 'Image', + help: 'A static image', + type: 'image', + expression: `image dataurl=null mode="contain" + | render`, + }, + markdown: { + name: 'markdown', + displayName: 'Text', + type: 'text', + help: 'Add text using Markdown', + icon: 'visText', + expression: `filters +| demodata +| markdown "### Welcome to the Markdown element + +Good news! You're already connected to some demo data! + +The data table contains +**{{rows.length}} rows**, each containing +the following columns: +{{#each columns}} +**{{name}}** +{{/each}} + +You can use standard Markdown in here, but you can also access your piped-in data using Handlebars. If you want to know more, check out the [Handlebars documentation](https://handlebarsjs.com/guide/expressions.html). + +#### Enjoy!" | render`, + }, + progressGauge: { + name: 'progressGauge', + displayName: 'Gauge', + type: 'progress', + help: 'Displays progress as a portion of a gauge', + width: 200, + height: 200, + icon: 'visGoal', + expression: `filters + | demodata + | math "mean(percent_uptime)" + | progress shape="gauge" label={formatnumber 0%} font={font size=24 family="Helvetica" color="#000000" align=center} + | render`, + }, + revealImage: { + name: 'revealImage', + displayName: 'Image reveal', + type: 'image', + help: 'Reveals a percentage of an image', + expression: `filters + | demodata + | math "mean(percent_uptime)" + | revealImage origin=bottom image=null + | render`, + }, + shape: { + name: 'shape', + displayName: 'Shape', + type: 'shape', + help: 'A customizable shape', + width: 200, + height: 200, + icon: 'node', + expression: + 'shape "square" fill="#4cbce4" border="rgba(255,255,255,0)" borderWidth=0 maintainAspect=false | render', + }, + table: { + name: 'table', + displayName: 'Data table', + type: 'chart', + help: 'A scrollable grid for displaying data in a tabular format', + expression: `filters + | demodata + | table + | render`, + }, + timeFilter: { + name: 'timeFilter', + displayName: 'Time filter', + type: 'filter', + help: 'Set a time window', + icon: 'calendar', + height: 50, + expression: `timefilterControl compact=true column=@timestamp + | render`, + filter: 'timefilter column=@timestamp from=now-24h to=now', + }, +}; + +const mockRenderEmbedPanel = () =>
; + +storiesOf('components/WorkpadHeader/ElementMenu', module).add('default', () => ( + +)); diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/element_menu/element_menu.scss b/x-pack/legacy/plugins/canvas/public/components/workpad_header/element_menu/element_menu.scss new file mode 100644 index 0000000000000..a946ee5519ce4 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/element_menu/element_menu.scss @@ -0,0 +1,3 @@ +.canvasElementMenu__popoverButton { + margin-right: $euiSizeS; +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/element_menu/element_menu.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/element_menu/element_menu.tsx new file mode 100644 index 0000000000000..a416adfe77469 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/element_menu/element_menu.tsx @@ -0,0 +1,216 @@ +/* + * 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 { sortBy } from 'lodash'; +import React, { Fragment, FunctionComponent, useState } from 'react'; +import PropTypes from 'prop-types'; +import { + EuiButton, + EuiContextMenu, + EuiIcon, + EuiContextMenuPanelItemDescriptor, +} from '@elastic/eui'; +import { CONTEXT_MENU_TOP_BORDER_CLASSNAME } from '../../../../common/lib'; +import { ComponentStrings } from '../../../../i18n/components'; +import { ElementSpec } from '../../../../types'; +import { flattenPanelTree } from '../../../lib/flatten_panel_tree'; +import { getId } from '../../../lib/get_id'; +import { Popover, ClosePopoverFn } from '../../popover'; +// @ts-ignore Untyped local +import { AssetManager } from '../../asset_manager'; +import { SavedElementsModal } from '../../saved_elements_modal'; + +interface CategorizedElementLists { + [key: string]: ElementSpec[]; +} + +interface ElementTypeMeta { + [key: string]: { name: string; icon: string }; +} + +export const { WorkpadHeaderElementMenu: strings } = ComponentStrings; + +// label and icon for the context menu item for each element type +const elementTypeMeta: ElementTypeMeta = { + chart: { name: strings.getChartMenuItemLabel(), icon: 'visArea' }, + filter: { name: strings.getFilterMenuItemLabel(), icon: 'filter' }, + image: { name: strings.getImageMenuItemLabel(), icon: 'image' }, + other: { name: strings.getOtherMenuItemLabel(), icon: 'empty' }, + progress: { name: strings.getProgressMenuItemLabel(), icon: 'visGoal' }, + shape: { name: strings.getShapeMenuItemLabel(), icon: 'node' }, + text: { name: strings.getTextMenuItemLabel(), icon: 'visText' }, +}; + +const getElementType = (element: ElementSpec): string => + element && element.type && Object.keys(elementTypeMeta).includes(element.type) + ? element.type + : 'other'; + +const categorizeElementsByType = (elements: ElementSpec[]): { [key: string]: ElementSpec[] } => { + elements = sortBy(elements, 'displayName'); + + const categories: CategorizedElementLists = { other: [] }; + + elements.forEach((element: ElementSpec) => { + const type = getElementType(element); + + if (categories[type]) { + categories[type].push(element); + } else { + categories[type] = [element]; + } + }); + + return categories; +}; + +export interface Props { + /** + * Dictionary of elements from elements registry + */ + elements: { [key: string]: ElementSpec }; + /** + * Handler for adding a selected element to the workpad + */ + addElement: (element: ElementSpec) => void; + /** + * Renders embeddable flyout + */ + renderEmbedPanel: (onClose: () => void) => JSX.Element; +} + +export const ElementMenu: FunctionComponent = ({ + elements, + addElement, + renderEmbedPanel, +}) => { + const [isAssetModalVisible, setAssetModalVisible] = useState(false); + const [isEmbedPanelVisible, setEmbedPanelVisible] = useState(false); + const [isSavedElementsModalVisible, setSavedElementsModalVisible] = useState(false); + + const hideAssetModal = () => setAssetModalVisible(false); + const showAssetModal = () => setAssetModalVisible(true); + const showEmbedPanel = () => setEmbedPanelVisible(false); + const hideEmbedPanel = () => setEmbedPanelVisible(false); + const hideSavedElementsModal = () => setSavedElementsModalVisible(false); + const showSavedElementsModal = () => setSavedElementsModalVisible(true); + + const { + chart: chartElements, + filter: filterElements, + image: imageElements, + other: otherElements, + progress: progressElements, + shape: shapeElements, + text: textElements, + } = categorizeElementsByType(Object.values(elements)); + + const getPanelTree = (closePopover: ClosePopoverFn) => { + const elementToMenuItem = (element: ElementSpec): EuiContextMenuPanelItemDescriptor => ({ + name: element.displayName || element.name, + icon: element.icon, + onClick: () => { + addElement(element); + closePopover(); + }, + }); + + const elementListToMenuItems = (elementList: ElementSpec[]) => { + const type = getElementType(elementList[0]); + const { name, icon } = elementTypeMeta[type] || elementTypeMeta.other; + + if (elementList.length > 1) { + return { + name, + icon: , + panel: { + id: getId('element-type'), + title: name, + items: elementList.map(elementToMenuItem), + }, + }; + } + + return elementToMenuItem(elementList[0]); + }; + + return { + id: 0, + title: strings.getElementMenuLabel(), + items: [ + elementListToMenuItems(textElements), + elementListToMenuItems(shapeElements), + elementListToMenuItems(chartElements), + elementListToMenuItems(imageElements), + elementListToMenuItems(filterElements), + elementListToMenuItems(progressElements), + elementListToMenuItems(otherElements), + { + name: strings.getMyElementsMenuItemLabel(), + className: CONTEXT_MENU_TOP_BORDER_CLASSNAME, + 'data-test-subj': 'saved-elements-menu-option', + icon: , + onClick: () => { + showSavedElementsModal(); + closePopover(); + }, + }, + { + name: strings.getAssetsMenuItemLabel(), + icon: , + onClick: () => { + showAssetModal(); + closePopover(); + }, + }, + { + name: strings.getEmbedObjectMenuItemLabel(), + className: CONTEXT_MENU_TOP_BORDER_CLASSNAME, + icon: , + onClick: () => { + showEmbedPanel(); + closePopover(); + }, + }, + ], + }; + }; + + const exportControl = (togglePopover: React.MouseEventHandler) => ( + + {strings.getElementMenuButtonLabel()} + + ); + + return ( + + + {({ closePopover }: { closePopover: ClosePopoverFn }) => ( + + )} + + {isAssetModalVisible ? : null} + {isEmbedPanelVisible ? renderEmbedPanel(hideEmbedPanel) : null} + {isSavedElementsModalVisible ? : null} + + ); +}; + +ElementMenu.propTypes = { + elements: PropTypes.object, + addElement: PropTypes.func.isRequired, +}; diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/element_menu/index.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/element_menu/index.tsx new file mode 100644 index 0000000000000..40571a9341f69 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/element_menu/index.tsx @@ -0,0 +1,49 @@ +/* + * 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 { connect } from 'react-redux'; +import { compose, withProps } from 'recompose'; +import { Dispatch } from 'redux'; +import { withKibana } from '../../../../../../../../src/plugins/kibana_react/public/'; +import { State, ElementSpec } from '../../../../types'; +// @ts-ignore Untyped local +import { elementsRegistry } from '../../../lib/elements_registry'; +import { ElementMenu as Component, Props as ComponentProps } from './element_menu'; +// @ts-ignore Untyped local +import { addElement } from '../../../state/actions/elements'; +import { getSelectedPage } from '../../../state/selectors/workpad'; +import { AddEmbeddablePanel } from '../../embeddable_flyout'; + +interface StateProps { + pageId: string; +} + +interface DispatchProps { + addElement: (pageId: string) => (partialElement: ElementSpec) => void; +} + +const mapStateToProps = (state: State) => ({ + pageId: getSelectedPage(state), +}); + +const mapDispatchToProps = (dispatch: Dispatch) => ({ + addElement: (pageId: string) => (element: ElementSpec) => dispatch(addElement(pageId, element)), +}); + +const mergeProps = (stateProps: StateProps, dispatchProps: DispatchProps) => ({ + ...stateProps, + ...dispatchProps, + addElement: dispatchProps.addElement(stateProps.pageId), + // Moved this section out of the main component to enable stories + renderEmbedPanel: (onClose: () => void) => , +}); + +export const ElementMenu = compose( + connect(mapStateToProps, mapDispatchToProps, mergeProps), + withKibana, + withProps(() => ({ elements: elementsRegistry.toJS() })) +)(Component); diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/__examples__/__snapshots__/pdf_panel.stories.storyshot b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/__examples__/__snapshots__/pdf_panel.stories.storyshot similarity index 93% rename from x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/__examples__/__snapshots__/pdf_panel.stories.storyshot rename to x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/__examples__/__snapshots__/pdf_panel.stories.storyshot index 43adcb37c5f4c..9ad2714a78ec9 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/__examples__/__snapshots__/pdf_panel.stories.storyshot +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/__examples__/__snapshots__/pdf_panel.stories.storyshot @@ -1,11 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Storyshots components/Export/PDFPanel default 1`] = ` +exports[`Storyshots components/WorkpadHeader/ShareMenu/PDFPanel default 1`] = `
+
+
+ +
+
+
+`; diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/__examples__/pdf_panel.stories.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/__examples__/pdf_panel.stories.tsx similarity index 92% rename from x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/__examples__/pdf_panel.stories.tsx rename to x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/__examples__/pdf_panel.stories.tsx index 76a40f51148a7..eb99dbc494a32 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/__examples__/pdf_panel.stories.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/__examples__/pdf_panel.stories.tsx @@ -8,7 +8,7 @@ import { action } from '@storybook/addon-actions'; import React from 'react'; import { PDFPanel } from '../pdf_panel'; -storiesOf('components/Export/PDFPanel', module) +storiesOf('components/WorkpadHeader/ShareMenu/PDFPanel', module) .addParameters({ info: { inline: true, diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/__examples__/workpad_export.examples.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/__examples__/share_menu.examples.tsx similarity index 79% rename from x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/__examples__/workpad_export.examples.tsx rename to x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/__examples__/share_menu.examples.tsx index 92e7cca40ee3a..ab9137b1676c9 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/__examples__/workpad_export.examples.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/__examples__/share_menu.examples.tsx @@ -6,10 +6,10 @@ import { storiesOf } from '@storybook/react'; import { action } from '@storybook/addon-actions'; import React from 'react'; -import { WorkpadExport } from '../workpad_export'; +import { ShareMenu } from '../share_menu'; -storiesOf('components/Export/WorkpadExport', module).add('enabled', () => ( - ( + { diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/flyout/__examples__/share_website_flyout.stories.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/flyout/__examples__/share_website_flyout.stories.tsx similarity index 93% rename from x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/flyout/__examples__/share_website_flyout.stories.tsx rename to x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/flyout/__examples__/share_website_flyout.stories.tsx index af30d8d4fc20b..886ddcfd763e1 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/flyout/__examples__/share_website_flyout.stories.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/flyout/__examples__/share_website_flyout.stories.tsx @@ -8,7 +8,7 @@ import { action } from '@storybook/addon-actions'; import React from 'react'; import { ShareWebsiteFlyout } from '../share_website_flyout'; -storiesOf('components/Export/ShareWebsiteFlyout', module) +storiesOf('components/WorkpadHeader/ShareMenu/ShareWebsiteFlyout', module) .addParameters({ info: { inline: true, diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/flyout/index.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/flyout/index.ts similarity index 91% rename from x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/flyout/index.ts rename to x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/flyout/index.ts index 2bf3e1f0ef1f4..6ab419656a7ee 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/flyout/index.ts +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/flyout/index.ts @@ -6,7 +6,6 @@ import { connect } from 'react-redux'; import { compose, withProps } from 'recompose'; -// @ts-ignore Untyped local import { getWorkpad, getRenderedWorkpad, @@ -14,26 +13,23 @@ import { } from '../../../../state/selectors/workpad'; // @ts-ignore Untyped local import { notify } from '../../../../lib/notify'; -// @ts-ignore Untyped local import { downloadRenderedWorkpad, downloadRuntime, downloadZippedRuntime, - // @ts-ignore Untyped local } from '../../../../lib/download_workpad'; import { ShareWebsiteFlyout as Component, Props as ComponentProps } from './share_website_flyout'; import { State, CanvasWorkpad } from '../../../../../types'; import { CanvasRenderedWorkpad } from '../../../../../shareable_runtime/types'; -// @ts-ignore Untyped local. -import { fetch, arrayBufferFetch } from '../../../../../common/lib/fetch'; +import { arrayBufferFetch } from '../../../../../common/lib/fetch'; import { API_ROUTE_SHAREABLE_ZIP } from '../../../../../common/lib/constants'; import { renderFunctionNames } from '../../../../../shareable_runtime/supported_renderers'; import { ComponentStrings } from '../../../../../i18n/components'; import { withKibana } from '../../../../../../../../../src/plugins/kibana_react/public/'; -import { OnCloseFn } from '../workpad_export'; +import { OnCloseFn } from '../share_menu'; import { WithKibanaProps } from '../../../../index'; -const { WorkpadHeaderWorkpadExport: strings } = ComponentStrings; +const { WorkpadHeaderShareMenu: strings } = ComponentStrings; const getUnsupportedRenderers = (state: State) => { const renderers: string[] = []; diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/flyout/runtime_step.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/flyout/runtime_step.tsx similarity index 100% rename from x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/flyout/runtime_step.tsx rename to x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/flyout/runtime_step.tsx diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/flyout/share_website_flyout.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/flyout/share_website_flyout.tsx similarity index 98% rename from x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/flyout/share_website_flyout.tsx rename to x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/flyout/share_website_flyout.tsx index 8dcbb18ffed86..5fd381baa73f5 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/flyout/share_website_flyout.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/flyout/share_website_flyout.tsx @@ -24,7 +24,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { ComponentStrings } from '../../../../../i18n/components'; import { ZIP, CANVAS, HTML } from '../../../../../i18n/constants'; -import { OnCloseFn } from '../workpad_export'; +import { OnCloseFn } from '../share_menu'; import { WorkpadStep } from './workpad_step'; import { RuntimeStep } from './runtime_step'; import { SnippetsStep } from './snippets_step'; diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/flyout/snippets_step.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/flyout/snippets_step.tsx similarity index 98% rename from x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/flyout/snippets_step.tsx rename to x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/flyout/snippets_step.tsx index c19ad6d77b131..81f559651eb25 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/flyout/snippets_step.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/flyout/snippets_step.tsx @@ -42,7 +42,7 @@ export const SnippetsStep: FC<{ onCopy: OnCopyFn }> = ({ onCopy }) => ( ({ workpad: getWorkpad(state), @@ -50,7 +43,7 @@ interface Props { pageCount: number; } -export const WorkpadExport = compose( +export const ShareMenu = compose( connect(mapStateToProps), withKibana, withProps( diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/pdf_panel.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/pdf_panel.tsx similarity index 92% rename from x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/pdf_panel.tsx rename to x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/pdf_panel.tsx index ef70079cf697b..a178964e0b566 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/pdf_panel.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/pdf_panel.tsx @@ -9,7 +9,7 @@ import { EuiButton, EuiSpacer, EuiText } from '@elastic/eui'; import { Clipboard } from '../../clipboard'; import { ComponentStrings } from '../../../../i18n/components'; -const { WorkpadHeaderWorkpadExport: strings } = ComponentStrings; +const { WorkpadHeaderShareMenu: strings } = ComponentStrings; interface Props { /** The URL that will invoke PDF Report generation. */ @@ -24,7 +24,7 @@ interface Props { * A panel displayed in the Export Menu with options in which to generate PDF Reports. */ export const PDFPanel = ({ pdfURL, onExport, onCopy }: Props) => ( -
+

{strings.getPDFPanelGenerateDescription()}

diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/workpad_export.scss b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/share_menu.scss similarity index 61% rename from x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/workpad_export.scss rename to x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/share_menu.scss index 44209aaa72d63..03227f77e0de5 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/workpad_export.scss +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/share_menu.scss @@ -1,8 +1,8 @@ -.canvasWorkpadExport__panelContent { +.canvasShareMenu__panelContent { padding: $euiSize; } -.canvasWorkpadExport__reportingConfig { +.canvasShareMenu__reportingConfig { .euiCodeBlock__pre { @include euiScrollBar; overflow-x: auto; diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/workpad_export.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/share_menu.tsx similarity index 71% rename from x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/workpad_export.tsx rename to x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/share_menu.tsx index 522be043ec457..621077c29c368 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/workpad_export.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/share_menu.tsx @@ -6,16 +6,14 @@ import React, { FunctionComponent, useState } from 'react'; import PropTypes from 'prop-types'; -import { EuiButtonIcon, EuiContextMenu, EuiIcon } from '@elastic/eui'; -// @ts-ignore Untyped local -import { Popover } from '../../popover'; +import { EuiButtonEmpty, EuiContextMenu, EuiIcon } from '@elastic/eui'; +import { ComponentStrings } from '../../../../i18n/components'; +import { flattenPanelTree } from '../../../lib/flatten_panel_tree'; +import { Popover, ClosePopoverFn } from '../../popover'; import { PDFPanel } from './pdf_panel'; import { ShareWebsiteFlyout } from './flyout'; -import { ComponentStrings } from '../../../../i18n/components'; -const { WorkpadHeaderWorkpadExport: strings } = ComponentStrings; - -type ClosePopoverFn = () => void; +const { WorkpadHeaderShareMenu: strings } = ComponentStrings; type CopyTypes = 'pdf' | 'reportingConfig'; type ExportTypes = 'pdf' | 'json'; @@ -39,31 +37,13 @@ export interface Props { /** * The Menu for Exporting a Workpad from Canvas. */ -export const WorkpadExport: FunctionComponent = ({ onCopy, onExport, getExportUrl }) => { +export const ShareMenu: FunctionComponent = ({ onCopy, onExport, getExportUrl }) => { const [showFlyout, setShowFlyout] = useState(false); const onClose = () => { setShowFlyout(false); }; - // TODO: Fix all of this magic from EUI; this code is boilerplate from - // EUI examples and isn't easily typed. - const flattenPanelTree = (tree: any, array: any[] = []) => { - array.push(tree); - - if (tree.items) { - tree.items.forEach((item: any) => { - const { panel } = item; - if (panel) { - flattenPanelTree(panel, array); - item.panel = panel.id; - } - }); - } - - return array; - }; - const getPDFPanel = (closePopover: ClosePopoverFn) => { return ( = ({ onCopy, onExport, getE ], }); - const exportControl = (togglePopover: React.MouseEventHandler) => ( - + const shareControl = (togglePopover: React.MouseEventHandler) => ( + + {strings.getShareMenuButtonLabel()} + ); const flyout = showFlyout ? : null; return (
- + {({ closePopover }: { closePopover: ClosePopoverFn }) => ( = ({ onCopy, onExport, getE ); }; -WorkpadExport.propTypes = { +ShareMenu.propTypes = { onCopy: PropTypes.func.isRequired, onExport: PropTypes.func.isRequired, getExportUrl: PropTypes.func.isRequired, diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.test.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/utils.test.ts similarity index 100% rename from x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.test.ts rename to x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/utils.test.ts diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/utils.ts similarity index 100% rename from x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts rename to x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/utils.ts diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/__examples__/__snapshots__/view_menu.stories.storyshot b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/__examples__/__snapshots__/view_menu.stories.storyshot new file mode 100644 index 0000000000000..e1ecee0e152be --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/__examples__/__snapshots__/view_menu.stories.storyshot @@ -0,0 +1,67 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Storyshots components/WorkpadHeader/ViewMenu edit mode 1`] = ` +
+
+ +
+
+`; + +exports[`Storyshots components/WorkpadHeader/ViewMenu read only mode 1`] = ` +
+
+ +
+
+`; diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/__examples__/view_menu.stories.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/__examples__/view_menu.stories.tsx new file mode 100644 index 0000000000000..60837ac1218e6 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/__examples__/view_menu.stories.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 { storiesOf } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; +import React from 'react'; +import { ViewMenu } from '../view_menu'; + +storiesOf('components/WorkpadHeader/ViewMenu', module) + .add('edit mode', () => ( + + )) + .add('read only mode', () => ( + + )); diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/index.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/index.ts new file mode 100644 index 0000000000000..c5aa8278ecf55 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/index.ts @@ -0,0 +1,85 @@ +/* + * 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 { connect } from 'react-redux'; +import { compose, withHandlers } from 'recompose'; +import { Dispatch } from 'redux'; +import { withKibana } from '../../../../../../../../src/plugins/kibana_react/public/'; +import { zoomHandlerCreators } from '../../../lib/app_handler_creators'; +// @ts-ignore Untyped local +import { notify } from '../../../lib/notify'; +import { State, CanvasWorkpadBoundingBox } from '../../../../types'; +// @ts-ignore Untyped local +import { fetchAllRenderables } from '../../../state/actions/elements'; +// @ts-ignore Untyped local +import { setZoomScale, setFullscreen, selectToplevelNodes } from '../../../state/actions/transient'; +// @ts-ignore Untyped local +import { setWriteable } from '../../../state/actions/workpad'; +import { getZoomScale, canUserWrite } from '../../../state/selectors/app'; +import { + getWorkpadBoundingBox, + getWorkpadWidth, + getWorkpadHeight, + isWriteable, +} from '../../../state/selectors/workpad'; +import { ViewMenu as Component, Props as ComponentProps } from './view_menu'; +import { getFitZoomScale } from './lib/get_fit_zoom_scale'; + +interface StateProps { + zoomScale: number; + boundingBox: CanvasWorkpadBoundingBox; + workpadWidth: number; + workpadHeight: number; + isWriteable: boolean; +} + +interface DispatchProps { + setWriteable: (isWorkpadWriteable: boolean) => void; + setZoomScale: (scale: number) => void; + setFullscreen: (showFullscreen: boolean) => void; +} + +const mapStateToProps = (state: State) => ({ + zoomScale: getZoomScale(state), + boundingBox: getWorkpadBoundingBox(state), + workpadWidth: getWorkpadWidth(state), + workpadHeight: getWorkpadHeight(state), + isWriteable: isWriteable(state) && canUserWrite(state), +}); + +const mapDispatchToProps = (dispatch: Dispatch) => ({ + setZoomScale: (scale: number) => dispatch(setZoomScale(scale)), + setWriteable: (isWorkpadWriteable: boolean) => dispatch(setWriteable(isWorkpadWriteable)), + setFullscreen: (value: boolean) => { + dispatch(setFullscreen(value)); + if (value) { + dispatch(selectToplevelNodes([])); + } + }, + doRefresh: () => dispatch(fetchAllRenderables()), +}); + +const mergeProps = ( + stateProps: StateProps, + dispatchProps: DispatchProps, + ownProps: ComponentProps +): ComponentProps => { + const { boundingBox, workpadWidth, workpadHeight, ...remainingStateProps } = stateProps; + return { + ...remainingStateProps, + ...dispatchProps, + ...ownProps, + toggleWriteable: () => dispatchProps.setWriteable(!stateProps.isWriteable), + enterFullscreen: () => dispatchProps.setFullscreen(true), + fitToWindow: () => getFitZoomScale(boundingBox, workpadWidth, workpadHeight), + }; +}; + +export const ViewMenu = compose( + connect(mapStateToProps, mapDispatchToProps, mergeProps), + withKibana, + withHandlers(zoomHandlerCreators) +)(Component); diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/lib/get_fit_zoom_scale.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/lib/get_fit_zoom_scale.ts new file mode 100644 index 0000000000000..783d6340c33c4 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/lib/get_fit_zoom_scale.ts @@ -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 { + CANVAS_LAYOUT_STAGE_CONTENT_SELECTOR, + WORKPAD_CANVAS_BUFFER, +} from '../../../../../common/lib'; +import { CanvasWorkpadBoundingBox } from '../../../../../types'; + +export const getFitZoomScale = ( + boundingBox: CanvasWorkpadBoundingBox, + workpadWidth: number, + workpadHeight: number +) => { + const canvasLayoutContent = document.querySelector( + `#${CANVAS_LAYOUT_STAGE_CONTENT_SELECTOR}` + ) as HTMLElement; + const layoutWidth = canvasLayoutContent.clientWidth; + const layoutHeight = canvasLayoutContent.clientHeight; + const offsetLeft = boundingBox.left; + const offsetTop = boundingBox.top; + const offsetRight = boundingBox.right - workpadWidth; + const offsetBottom = boundingBox.bottom - workpadHeight; + const boundingWidth = + workpadWidth + + Math.max(Math.abs(offsetLeft), Math.abs(offsetRight)) * 2 + + WORKPAD_CANVAS_BUFFER; + const boundingHeight = + workpadHeight + + Math.max(Math.abs(offsetTop), Math.abs(offsetBottom)) * 2 + + WORKPAD_CANVAS_BUFFER; + const xScale = layoutWidth / boundingWidth; + const yScale = layoutHeight / boundingHeight; + + return Math.min(xScale, yScale); +}; diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/view_menu.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/view_menu.tsx new file mode 100644 index 0000000000000..d1e08c5809579 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/view_menu.tsx @@ -0,0 +1,172 @@ +/* + * 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, { FunctionComponent } from 'react'; +import PropTypes from 'prop-types'; +import { + EuiButtonEmpty, + EuiContextMenu, + EuiIcon, + EuiContextMenuPanelItemDescriptor, +} from '@elastic/eui'; +import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from '../../../../common/lib/constants'; +import { ComponentStrings } from '../../../../i18n/components'; +import { flattenPanelTree } from '../../../lib/flatten_panel_tree'; +import { Popover, ClosePopoverFn } from '../../popover'; + +const { WorkpadHeaderViewMenu: strings } = ComponentStrings; + +const QUICK_ZOOM_LEVELS = [0.5, 1, 2]; + +export interface Props { + /** + * Is the workpad edittable? + */ + isWriteable: boolean; + /** + * current workpad zoom level + */ + zoomScale: number; + /** + * zooms to fit entire workpad into view + */ + fitToWindow: () => void; + /** + * handler to set the workpad zoom level to a specific value + */ + setZoomScale: (scale: number) => void; + /** + * handler to increase the workpad zoom level + */ + zoomIn: () => void; + /** + * handler to decrease workpad zoom level + */ + zoomOut: () => void; + /** + * reset zoom to 100% + */ + resetZoom: () => void; + /** + * toggle edit/read only mode + */ + toggleWriteable: () => void; + /** + * enter fullscreen mode + */ + enterFullscreen: () => void; + /** + * triggers a refresh of the workpad + */ + doRefresh: () => void; +} + +export const ViewMenu: FunctionComponent = ({ + doRefresh, + enterFullscreen, + fitToWindow, + isWriteable, + resetZoom, + setZoomScale, + toggleWriteable, + zoomIn, + zoomOut, + zoomScale, +}) => { + const viewControl = (togglePopover: React.MouseEventHandler) => ( + + {strings.getViewMenuButtonLabel()} + + ); + + const getScaleMenuItems = (): EuiContextMenuPanelItemDescriptor[] => + QUICK_ZOOM_LEVELS.map((scale: number) => ({ + name: strings.getZoomPercentage(scale), + icon: 'empty', + onClick: () => setZoomScale(scale), + })); + + const getZoomMenuItems = (): EuiContextMenuPanelItemDescriptor[] => [ + { + name: strings.getZoomFitToWindowText(), + icon: 'empty', + onClick: fitToWindow, + disabled: zoomScale === MAX_ZOOM_LEVEL, + }, + ...getScaleMenuItems(), + { + name: strings.getZoomInText(), + icon: 'magnifyWithPlus', + onClick: zoomIn, + disabled: zoomScale === MAX_ZOOM_LEVEL, + className: 'canvasContextMenu--topBorder', + }, + { + name: strings.getZoomOutText(), + icon: 'magnifyWithMinus', + onClick: zoomOut, + disabled: zoomScale <= MIN_ZOOM_LEVEL, + }, + { + name: strings.getZoomResetText(), + icon: 'empty', + onClick: resetZoom, + disabled: zoomScale >= MAX_ZOOM_LEVEL, + className: 'canvasContextMenu--topBorder', + }, + ]; + + const getPanelTree = (closePopover: ClosePopoverFn) => ({ + id: 0, + title: strings.getViewMenuLabel(), + items: [ + { + name: strings.getFullscreenMenuItemLabel(), + icon: , + onClick: () => { + enterFullscreen(); + closePopover(); + }, + }, + { + name: isWriteable ? strings.getHideEditModeLabel() : strings.getShowEditModeLabel(), + icon: , + onClick: () => { + toggleWriteable(); + closePopover(); + }, + }, + { + name: strings.getRefreshMenuItemLabel(), + icon: 'refresh', + onClick: () => { + doRefresh(); + }, + }, + { + name: strings.getZoomMenuItemLabel(), + icon: 'magnifyWithPlus', + panel: { + id: 1, + title: strings.getZoomMenuItemLabel(), + items: getZoomMenuItems(), + }, + }, + ], + }); + + return ( + + {({ closePopover }: { closePopover: ClosePopoverFn }) => ( + + )} + + ); +}; + +ViewMenu.propTypes = { + isWriteable: PropTypes.bool.isRequired, +}; diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/__examples__/__snapshots__/workpad_export.examples.storyshot b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/__examples__/__snapshots__/workpad_export.examples.storyshot deleted file mode 100644 index ef96320e7bc65..0000000000000 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/__examples__/__snapshots__/workpad_export.examples.storyshot +++ /dev/null @@ -1,41 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Storyshots components/Export/WorkpadExport enabled 1`] = ` -
-
-
- - - -
-
-
-`; diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_header.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_header.tsx index 31ad0593f58bb..253e6c68cfc9e 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_header.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_header.tsx @@ -4,38 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { FunctionComponent } from 'react'; import PropTypes from 'prop-types'; // @ts-ignore no @types definition import { Shortcuts } from 'react-shortcuts'; -import { - EuiFlexItem, - EuiFlexGroup, - EuiButtonIcon, - EuiButton, - EuiButtonEmpty, - EuiOverlayMask, - EuiModal, - EuiModalFooter, - EuiToolTip, -} from '@elastic/eui'; - +import { EuiFlexItem, EuiFlexGroup, EuiButtonIcon, EuiToolTip } from '@elastic/eui'; import { ComponentStrings } from '../../../i18n'; - -// @ts-ignore untyped local -import { AssetManager } from '../asset_manager'; -// @ts-ignore untyped local -import { ElementTypes } from '../element_types'; import { ToolTipShortcut } from '../tool_tip_shortcut/'; -import { AddEmbeddablePanel } from '../embeddable_flyout'; -// @ts-ignore untyped local import { ControlSettings } from './control_settings'; // @ts-ignore untyped local import { RefreshControl } from './refresh_control'; // @ts-ignore untyped local import { FullscreenControl } from './fullscreen_control'; -import { WorkpadExport } from './workpad_export'; -import { WorkpadZoom } from './workpad_zoom'; +import { ElementMenu } from './element_menu'; +import { ShareMenu } from './share_menu'; +import { ViewMenu } from './view_menu'; const { WorkpadHeader: strings } = ComponentStrings; @@ -43,23 +26,20 @@ export interface Props { isWriteable: boolean; toggleWriteable: () => void; canUserWrite: boolean; - selectedPage: string; } -interface State { - isModalVisible: boolean; - isPanelVisible: boolean; -} - -export class WorkpadHeader extends React.PureComponent { - static propTypes = { - isWriteable: PropTypes.bool, - toggleWriteable: PropTypes.func, +export const WorkpadHeader: FunctionComponent = ({ + isWriteable, + canUserWrite, + toggleWriteable, +}) => { + const keyHandler = (action: string) => { + if (action === 'EDITING') { + toggleWriteable(); + } }; - state = { isModalVisible: false, isPanelVisible: false }; - - _fullscreenButton = ({ toggleFullscreen }: { toggleFullscreen: () => void }) => ( + const fullscreenButton = ({ toggleFullscreen }: { toggleFullscreen: () => void }) => ( { ); - _keyHandler = (action: string) => { - if (action === 'EDITING') { - this.props.toggleWriteable(); - } - }; - - _hideElementModal = () => this.setState({ isModalVisible: false }); - _showElementModal = () => this.setState({ isModalVisible: true }); - - _hideEmbeddablePanel = () => this.setState({ isPanelVisible: false }); - _showEmbeddablePanel = () => this.setState({ isPanelVisible: true }); - - _elementAdd = () => ( - - - - - - {strings.getAddElementModalCloseButtonLabel()} - - - - - ); - - _embeddableAdd = () => ; - - _getEditToggleToolTipText = () => { - if (!this.props.canUserWrite) { + const getEditToggleToolTipText = () => { + if (!canUserWrite) { return strings.getNoWritePermissionTooltipText(); } - const content = this.props.isWriteable + const content = isWriteable ? strings.getHideEditControlTooltip() : strings.getShowEditControlTooltip(); return content; }; - _getEditToggleToolTip = ({ textOnly } = { textOnly: false }) => { - const content = this._getEditToggleToolTipText(); + const getEditToggleToolTip = ({ textOnly } = { textOnly: false }) => { + const content = getEditToggleToolTipText(); if (textOnly) { return content; @@ -135,86 +83,67 @@ export class WorkpadHeader extends React.PureComponent { ); }; - render() { - const { isWriteable, canUserWrite, toggleWriteable } = this.props; - const { isModalVisible, isPanelVisible } = this.state; - - return ( -
- {isModalVisible ? this._elementAdd() : null} - {isPanelVisible ? this._embeddableAdd() : null} - - - - - - - - - - - {this._fullscreenButton} - - - - - - - - - {canUserWrite && ( - - )} - - - - - - - {isWriteable ? ( + return ( + + + + {isWriteable && ( - - - - - - - {strings.getEmbedObjectButtonLabel()} - - - - - {strings.getAddElementButtonLabel()} - - - + - ) : null} + )} + + + + + + + + + -
- ); - } -} + + + + + {canUserWrite && ( + + )} + + + + + + + + + {fullscreenButton} + + + + + ); +}; + +WorkpadHeader.propTypes = { + isWriteable: PropTypes.bool, + toggleWriteable: PropTypes.func, + canUserWrite: PropTypes.bool, +}; diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_zoom/index.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_zoom/index.tsx deleted file mode 100644 index b22a9d35aa793..0000000000000 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_zoom/index.tsx +++ /dev/null @@ -1,38 +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 { compose, withHandlers } from 'recompose'; -import { connect } from 'react-redux'; -import { Dispatch } from 'redux'; -import { getZoomScale } from '../../../state/selectors/app'; -import { - getWorkpadBoundingBox, - getWorkpadWidth, - getWorkpadHeight, -} from '../../../state/selectors/workpad'; -// @ts-ignore unconverted local file -import { setZoomScale } from '../../../state/actions/transient'; -import { zoomHandlerCreators } from '../../../lib/app_handler_creators'; -import { WorkpadZoom as Component, Props as ComponentProps } from './workpad_zoom'; -import { State } from '../../../../types'; - -const mapStateToProps = (state: State) => { - return { - zoomScale: getZoomScale(state), - boundingBox: getWorkpadBoundingBox(state), - workpadWidth: getWorkpadWidth(state), - workpadHeight: getWorkpadHeight(state), - }; -}; - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - setZoomScale: (scale: number) => dispatch(setZoomScale(scale)), -}); - -export const WorkpadZoom = compose( - connect(mapStateToProps, mapDispatchToProps), - withHandlers(zoomHandlerCreators) -)(Component); diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_zoom/workpad_zoom.scss b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_zoom/workpad_zoom.scss deleted file mode 100644 index 44209aaa72d63..0000000000000 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_zoom/workpad_zoom.scss +++ /dev/null @@ -1,11 +0,0 @@ -.canvasWorkpadExport__panelContent { - padding: $euiSize; -} - -.canvasWorkpadExport__reportingConfig { - .euiCodeBlock__pre { - @include euiScrollBar; - overflow-x: auto; - white-space: pre; - } -} diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_zoom/workpad_zoom.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_zoom/workpad_zoom.tsx deleted file mode 100644 index 4e37a525761cd..0000000000000 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_zoom/workpad_zoom.tsx +++ /dev/null @@ -1,176 +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, { MouseEventHandler, PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import { - EuiButtonIcon, - EuiContextMenu, - EuiContextMenuPanelDescriptor, - EuiContextMenuPanelItemDescriptor, -} from '@elastic/eui'; -// @ts-ignore untyped local -import { Popover } from '../../popover'; -import { - MAX_ZOOM_LEVEL, - MIN_ZOOM_LEVEL, - WORKPAD_CANVAS_BUFFER, - CANVAS_LAYOUT_STAGE_CONTENT_SELECTOR, -} from '../../../../common/lib/constants'; - -import { ComponentStrings } from '../../../../i18n'; -const { WorkpadHeaderWorkpadZoom: strings } = ComponentStrings; - -export interface Props { - /** - * current workpad zoom level - */ - zoomScale: number; - /** - * minimum bounding box for the workpad - */ - boundingBox: { left: number; right: number; top: number; bottom: number }; - /** - * width of the workpad page - */ - workpadWidth: number; - /** - * height of the workpad page - */ - workpadHeight: number; - /** - * handler to set the workpad zoom level to a specific value - */ - setZoomScale: (scale: number) => void; - /** - * handler to increase the workpad zoom level - */ - zoomIn: () => void; - /** - * handler to decrease workpad zoom level - */ - zoomOut: () => void; - /** - * reset zoom to 100% - */ - resetZoom: () => void; -} - -const QUICK_ZOOM_LEVELS = [0.5, 1, 2]; - -export class WorkpadZoom extends PureComponent { - static propTypes = { - zoomScale: PropTypes.number.isRequired, - setZoomScale: PropTypes.func.isRequired, - zoomIn: PropTypes.func.isRequired, - zoomOut: PropTypes.func.isRequired, - resetZoom: PropTypes.func.isRequired, - boundingBox: PropTypes.shape({ - left: PropTypes.number.isRequired, - right: PropTypes.number.isRequired, - top: PropTypes.number.isRequired, - bottom: PropTypes.number.isRequired, - }), - workpadWidth: PropTypes.number.isRequired, - workpadHeight: PropTypes.number.isRequired, - }; - - _fitToWindow = () => { - const { boundingBox, setZoomScale, workpadWidth, workpadHeight } = this.props; - const canvasLayoutContent = document.querySelector( - `#${CANVAS_LAYOUT_STAGE_CONTENT_SELECTOR}` - ) as HTMLElement; - const layoutWidth = canvasLayoutContent.clientWidth; - const layoutHeight = canvasLayoutContent.clientHeight; - const offsetLeft = boundingBox.left; - const offsetTop = boundingBox.top; - const offsetRight = boundingBox.right - workpadWidth; - const offsetBottom = boundingBox.bottom - workpadHeight; - const boundingWidth = - workpadWidth + - Math.max(Math.abs(offsetLeft), Math.abs(offsetRight)) * 2 + - WORKPAD_CANVAS_BUFFER; - const boundingHeight = - workpadHeight + - Math.max(Math.abs(offsetTop), Math.abs(offsetBottom)) * 2 + - WORKPAD_CANVAS_BUFFER; - const xScale = layoutWidth / boundingWidth; - const yScale = layoutHeight / boundingHeight; - - setZoomScale(Math.min(xScale, yScale)); - }; - - _button = (togglePopover: MouseEventHandler) => ( - - ); - - _getScaleMenuItems = (): EuiContextMenuPanelItemDescriptor[] => - QUICK_ZOOM_LEVELS.map(scale => ({ - name: strings.getZoomPercentage(scale), - icon: 'empty', - onClick: () => this.props.setZoomScale(scale), - })); - - _getPanels = (): EuiContextMenuPanelDescriptor[] => { - const { zoomScale, zoomIn, zoomOut, resetZoom } = this.props; - const items: EuiContextMenuPanelItemDescriptor[] = [ - { - name: strings.getZoomFitToWindowText(), - icon: 'empty', - onClick: this._fitToWindow, - disabled: zoomScale === MAX_ZOOM_LEVEL, - }, - ...this._getScaleMenuItems(), - { - name: strings.getZoomInText(), - icon: 'magnifyWithPlus', - onClick: zoomIn, - disabled: zoomScale === MAX_ZOOM_LEVEL, - className: 'canvasContextMenu--topBorder', - }, - { - name: strings.getZoomOutText(), - icon: 'magnifyWithMinus', - onClick: zoomOut, - disabled: zoomScale <= MIN_ZOOM_LEVEL, - }, - { - name: strings.getZoomResetText(), - icon: 'empty', - onClick: resetZoom, - disabled: zoomScale >= MAX_ZOOM_LEVEL, - className: 'canvasContextMenu--topBorder', - }, - ]; - - const panels: EuiContextMenuPanelDescriptor[] = [ - { - id: 0, - title: strings.getZoomPanelTitle(), - items, - }, - ]; - - return panels; - }; - - render() { - return ( - - {() => } - - ); - } -} diff --git a/x-pack/legacy/plugins/canvas/public/lib/custom_element_service.ts b/x-pack/legacy/plugins/canvas/public/lib/custom_element_service.ts index 478e2f8f18cf5..4118bb81b8363 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/custom_element_service.ts +++ b/x-pack/legacy/plugins/canvas/public/lib/custom_element_service.ts @@ -5,9 +5,7 @@ */ import { AxiosPromise } from 'axios'; -// @ts-ignore unconverted local file import { API_ROUTE_CUSTOM_ELEMENT } from '../../common/lib/constants'; -// @ts-ignore unconverted local file import { fetch } from '../../common/lib/fetch'; import { CustomElement } from '../../types'; import { getCoreStart } from '../legacy'; @@ -25,7 +23,7 @@ export const get = (customElementId: string): Promise => .get(`${getApiPath()}/${customElementId}`) .then(({ data: element }: { data: CustomElement }) => element); -export const update = (id: string, element: CustomElement): AxiosPromise => +export const update = (id: string, element: Partial): AxiosPromise => fetch.put(`${getApiPath()}/${id}`, element); export const remove = (id: string): AxiosPromise => fetch.delete(`${getApiPath()}/${id}`); diff --git a/x-pack/legacy/plugins/canvas/public/lib/default_header.png b/x-pack/legacy/plugins/canvas/public/lib/default_header.png deleted file mode 100644 index 0b5c5b8f58f9bda5a2bf7a7909ed457bae7132a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 623 zcmeAS@N?(olHy`uVBq!ia0y~yV4MJCFJNH?k{9NvKLb)M>5jgR3=A9lx&I`x0{M)^ zLGDfr>(0r%1agD|d_r8^3w#(VmN8T+P(weun}C+`Q|*EhytxyKDaMP2izttLozOg^JFd|7x6<4wGcmJ+Hpd!e`c+)o=VUB(nUy(lZ&q7LzyIv0t=lGr z?f)$itiNdCjh>=p>y9@f>DLbKtl(T){ccL&!W$|_UpSomRloAOQf696ljo)F9rGtz zZJ%;$<(ehiz7_4Q`*%J6?APEO8FSs^Rn>!jJYG40d(zDR&EA$1kLfRr(G%SMujG>G z&ONfT#PzuR`j{VMqgB;@Jp=d#Wzp$Py@B@h+> diff --git a/x-pack/legacy/plugins/canvas/public/lib/element.ts b/x-pack/legacy/plugins/canvas/public/lib/element.ts index 121c253668ed9..ef1cf601b6e26 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/element.ts +++ b/x-pack/legacy/plugins/canvas/public/lib/element.ts @@ -5,18 +5,16 @@ */ import { ElementSpec } from '../../types'; -import defaultHeader from './default_header.png'; -import { tagsRegistry } from './tags_registry'; export class Element { /** The name of the Element. This must match the name of the function that is used to create the `type: render` object */ public name: string; /** A more friendly name for the Element */ public displayName: string; - /** Relevant labels to help identify the elements */ - public tags: string[]; - /** An image to use in the Element type selector */ - public image: string; + /** The type of the Element */ + public type?: string; + /** The name of the EUI icon to display in the element menu */ + public icon: string; /** A sentence or few about what this Element does */ public help: string; /** A default expression that allows Canvas to render the Element */ @@ -28,23 +26,17 @@ export class Element { public height?: number; constructor(config: ElementSpec) { - const { name, image, displayName, tags, expression, filter, help, width, height } = config; + const { name, icon, displayName, type, expression, filter, help, width, height } = config; this.name = name; this.displayName = displayName || name; - this.image = image || defaultHeader; + this.icon = icon || 'empty'; this.help = help || ''; if (!config.expression) { throw new Error('Element types must have a default expression'); } - this.tags = tags || []; - - this.tags.forEach(tag => { - if (!tagsRegistry.get(tag)) { - tagsRegistry.register(() => ({ name: tag, color: '#666666' })); - } - }); + this.type = type; this.expression = expression; this.filter = filter; this.width = width || 500; diff --git a/x-pack/legacy/plugins/canvas/public/lib/flatten_panel_tree.ts b/x-pack/legacy/plugins/canvas/public/lib/flatten_panel_tree.ts new file mode 100644 index 0000000000000..a059d07725727 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/lib/flatten_panel_tree.ts @@ -0,0 +1,23 @@ +/* + * 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. + */ + +// TODO: Fix all of this magic from EUI; this code is boilerplate from +// EUI examples and isn't easily typed. +export const flattenPanelTree = (tree: any, array: any[] = []) => { + array.push(tree); + + if (tree.items) { + tree.items.forEach((item: any) => { + const { panel } = item; + if (panel) { + flattenPanelTree(panel, array); + item.panel = panel.id; + } + }); + } + + return array; +}; diff --git a/x-pack/legacy/plugins/canvas/public/state/selectors/workpad.ts b/x-pack/legacy/plugins/canvas/public/state/selectors/workpad.ts index 84fab0cb0ae6d..1623035bd25ba 100644 --- a/x-pack/legacy/plugins/canvas/public/state/selectors/workpad.ts +++ b/x-pack/legacy/plugins/canvas/public/state/selectors/workpad.ts @@ -11,7 +11,12 @@ import { safeElementFromExpression, fromExpression } from '@kbn/interpreter/comm import { append } from '../../lib/modify_path'; import { getAssets } from './assets'; import { State, CanvasWorkpad, CanvasPage, CanvasElement, ResolvedArgType } from '../../../types'; -import { ExpressionContext, CanvasGroup, PositionedElement } from '../../../types'; +import { + ExpressionContext, + CanvasGroup, + PositionedElement, + CanvasWorkpadBoundingBox, +} from '../../../types'; import { ExpressionAstArgument, ExpressionAstFunction, @@ -91,7 +96,7 @@ export function getWorkpadWidth(state: State): number { return get(state, append(workpadRoot, 'width')); } -export function getWorkpadBoundingBox(state: State) { +export function getWorkpadBoundingBox(state: State): CanvasWorkpadBoundingBox { return getPages(state).reduce( (boundingBox, page) => { page.elements.forEach(({ position }) => { diff --git a/x-pack/legacy/plugins/canvas/public/style/index.scss b/x-pack/legacy/plugins/canvas/public/style/index.scss index 56f9ed8d18cbe..ba0845862368a 100644 --- a/x-pack/legacy/plugins/canvas/public/style/index.scss +++ b/x-pack/legacy/plugins/canvas/public/style/index.scss @@ -52,7 +52,8 @@ @import '../components/tooltip_annotation/tooltip_annotation'; @import '../components/workpad/workpad'; @import '../components/workpad_header/control_settings/control_settings'; -@import '../components/workpad_header/workpad_export/workpad_export'; +@import '../components/workpad_header/element_menu/element_menu'; +@import '../components/workpad_header/share_menu/share_menu'; @import '../components/workpad_loader/workpad_loader'; @import '../components/workpad_loader/workpad_dropzone/workpad_dropzone'; @import '../components/workpad_page/workpad_page'; diff --git a/x-pack/legacy/plugins/canvas/shareable_runtime/test/workpads/austin.json b/x-pack/legacy/plugins/canvas/shareable_runtime/test/workpads/austin.json index f9f999440c7ce..b725afab2b10f 100644 --- a/x-pack/legacy/plugins/canvas/shareable_runtime/test/workpads/austin.json +++ b/x-pack/legacy/plugins/canvas/shareable_runtime/test/workpads/austin.json @@ -28878,7 +28878,7 @@ "type": "render", "as": "markdown", "value": { - "content": "```\nexport const githubLimitGauge = () => ({\n name: 'githubLimitGauge',\n displayName: 'Github Limit Gauge',\n help: 'A progress pill displaying...',\n image: header,\n expression: `github_rate_limit \n | filterrows fn={getCell name | eq core} \n | if \n condition={math limit | eq 0} \n then=0 \n else={math \"remaining / limit\"}\n | progress \n label=\"Core\"\n shape=\"horizontalPill\" \n | render\n `,\n };\n}\n```", + "content": "```\nexport const githubLimitGauge = () => ({\n name: 'githubLimitGauge',\n displayName: 'Github Limit Gauge',\n help: 'A progress pill displaying...',\n expression: `github_rate_limit \n | filterrows fn={getCell name | eq core} \n | if \n condition={math limit | eq 0} \n then=0 \n else={math \"remaining / limit\"}\n | progress \n label=\"Core\"\n shape=\"horizontalPill\" \n | render\n `,\n };\n}\n```", "font": { "type": "style", "spec": { @@ -28919,7 +28919,7 @@ "type": "render", "as": "markdown", "value": { - "content": "```\nexport function randomNumber() {\n return {\n name: 'randomNumber',\n displayName: 'Random Number',\n help: 'A random number between 0 and 1.',\n image: header,\n expression: \n 'random \n | math \"round(value, 3)\" \n | metric \"Random Number\"\n ',\n };\n}\n```", + "content": "```\nexport function randomNumber() {\n return {\n name: 'randomNumber',\n displayName: 'Random Number',\n help: 'A random number between 0 and 1.',\n expression: \n 'random \n | math \"round(value, 3)\" \n | metric \"Random Number\"\n ',\n };\n}\n```", "font": { "type": "style", "spec": { diff --git a/x-pack/legacy/plugins/canvas/tasks/mocks/customElementService.js b/x-pack/legacy/plugins/canvas/tasks/mocks/customElementService.js new file mode 100644 index 0000000000000..3162638cb6c5d --- /dev/null +++ b/x-pack/legacy/plugins/canvas/tasks/mocks/customElementService.js @@ -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. + */ + +export const testCustomElements = [ + { + id: 'custom-element-10d625f5-1342-47c9-8f19-d174ea6b65d5', + name: 'customElement1', + displayName: 'Custom Element 1', + help: 'sample description', + image: '', + content: `{\"selectedNodes\":[{\"id\":\"element-3383b40a-de5d-4efb-8719-f4d8cffbfa74\",\"position\":{\"left\":142,\"top\":146,\"width\":700,\"height\":300,\"angle\":0,\"parent\":null,\"type\":\"element\"},\"expression\":\"filters\\n| demodata\\n| pointseries x=\\\"project\\\" y=\\\"sum(price)\\\" color=\\\"state\\\" size=\\\"size(username)\\\"\\n| plot defaultStyle={seriesStyle points=5 fill=1}\\n| render\",\"ast\":{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"filters\",\"arguments\":{}},{\"type\":\"function\",\"function\":\"demodata\",\"arguments\":{}},{\"type\":\"function\",\"function\":\"pointseries\",\"arguments\":{\"x\":[\"project\"],\"y\":[\"sum(price)\"],\"color\":[\"state\"],\"size\":[\"size(username)\"]}},{\"type\":\"function\",\"function\":\"plot\",\"arguments\":{\"defaultStyle\":[{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"seriesStyle\",\"arguments\":{\"points\":[5],\"fill\":[1]}}]}]}},{\"type\":\"function\",\"function\":\"render\",\"arguments\":{}}]}}]}`, + }, + { + id: 'custom-element-b22d8d10-6116-46fb-9b46-c3f3340d3aaa', + name: 'customElement2', + displayName: 'Custom Element 2', + help: 'Aenean eu justo auctor, placerat felis non, scelerisque dolor. ', + image: '', + content: `{\"selectedNodes\":[{\"id\":\"group-dccf4ed7-1593-49a0-9902-caf4d4a4b7f5\",\"position\":{\"left\":250,\"top\":119,\"width\":340,\"height\":517,\"angle\":0,\"parent\":null,\"type\":\"group\"},\"expression\":\"shape fill=\\\"rgba(255,255,255,0)\\\" | render\",\"ast\":{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"shape\",\"arguments\":{\"fill\":[\"rgba(255,255,255,0)\"]}},{\"type\":\"function\",\"function\":\"render\",\"arguments\":{}}]}},{\"id\":\"element-e2c658ee-7614-4d92-a46e-2b1a81a24485\",\"position\":{\"left\":250,\"top\":405,\"width\":340,\"height\":75,\"angle\":0,\"parent\":\"group-dccf4ed7-1593-49a0-9902-caf4d4a4b7f5\",\"type\":\"element\"},\"expression\":\"filters\\n| demodata\\n| markdown \\\"## Jane Doe\\\" \\n font={font family=\\\"'Open Sans', Helvetica, Arial, sans-serif\\\" size=14 align=\\\"center\\\" color=\\\"#000000\\\" weight=\\\"normal\\\" underline=false italic=false}\\n| render\",\"ast\":{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"filters\",\"arguments\":{}},{\"type\":\"function\",\"function\":\"demodata\",\"arguments\":{}},{\"type\":\"function\",\"function\":\"markdown\",\"arguments\":{\"_\":[\"## Jane Doe\"],\"font\":[{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"font\",\"arguments\":{\"family\":[\"'Open Sans', Helvetica, Arial, sans-serif\"],\"size\":[14],\"align\":[\"center\"],\"color\":[\"#000000\"],\"weight\":[\"normal\"],\"underline\":[false],\"italic\":[false]}}]}]}},{\"type\":\"function\",\"function\":\"render\",\"arguments\":{}}]}},{\"id\":\"element-3d16765e-5251-4954-8e2a-6c64ed465b73\",\"position\":{\"left\":250,\"top\":480,\"width\":340,\"height\":75,\"angle\":0,\"parent\":\"group-dccf4ed7-1593-49a0-9902-caf4d4a4b7f5\",\"type\":\"element\"},\"expression\":\"filters\\n| demodata\\n| markdown \\\"### Developer\\\" \\n font={font family=\\\"'Open Sans', Helvetica, Arial, sans-serif\\\" size=14 align=\\\"center\\\" color=\\\"#000000\\\" weight=\\\"normal\\\" underline=false italic=false}\\n| render css=\\\".canvasRenderEl h3 {\\ncolor: #444444;\\n}\\\"\",\"ast\":{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"filters\",\"arguments\":{}},{\"type\":\"function\",\"function\":\"demodata\",\"arguments\":{}},{\"type\":\"function\",\"function\":\"markdown\",\"arguments\":{\"_\":[\"### Developer\"],\"font\":[{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"font\",\"arguments\":{\"family\":[\"'Open Sans', Helvetica, Arial, sans-serif\"],\"size\":[14],\"align\":[\"center\"],\"color\":[\"#000000\"],\"weight\":[\"normal\"],\"underline\":[false],\"italic\":[false]}}]}]}},{\"type\":\"function\",\"function\":\"render\",\"arguments\":{\"css\":[\".canvasRenderEl h3 {\\ncolor: #444444;\\n}\"]}}]}},{\"id\":\"element-624675cf-46e9-4545-b86a-5409bbe53ac1\",\"position\":{\"left\":250,\"top\":555,\"width\":340,\"height\":81,\"angle\":0,\"parent\":\"group-dccf4ed7-1593-49a0-9902-caf4d4a4b7f5\",\"type\":\"element\"},\"expression\":\"filters\\n| demodata\\n| markdown \\n \\\"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vel sollicitudin mauris, ut scelerisque urna. \\\" \\n font={font family=\\\"'Open Sans', Helvetica, Arial, sans-serif\\\" size=14 align=\\\"center\\\" color=\\\"#000000\\\" weight=\\\"normal\\\" underline=false italic=false}\\n| render\",\"ast\":{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"filters\",\"arguments\":{}},{\"type\":\"function\",\"function\":\"demodata\",\"arguments\":{}},{\"type\":\"function\",\"function\":\"markdown\",\"arguments\":{\"_\":[\"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vel sollicitudin mauris, ut scelerisque urna. \"],\"font\":[{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"font\",\"arguments\":{\"family\":[\"'Open Sans', Helvetica, Arial, sans-serif\"],\"size\":[14],\"align\":[\"center\"],\"color\":[\"#000000\"],\"weight\":[\"normal\"],\"underline\":[false],\"italic\":[false]}}]}]}},{\"type\":\"function\",\"function\":\"render\",\"arguments\":{}}]}},{\"id\":\"element-c2916246-26dd-4c65-91c6-d1ad3f1791ee\",\"position\":{\"left\":293,\"top\":119,\"width\":254,\"height\":252,\"angle\":0,\"parent\":\"group-dccf4ed7-1593-49a0-9902-caf4d4a4b7f5\",\"type\":\"element\"},\"expression\":\"image dataurl={asset \\\"asset-0c6f377f-771e-432e-8e2e-15c3e9142ad6\\\"} mode=\\\"contain\\\"\\n| render\",\"ast\":{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"image\",\"arguments\":{\"dataurl\":[{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"asset\",\"arguments\":{\"_\":[\"asset-0c6f377f-771e-432e-8e2e-15c3e9142ad6\"]}}]}],\"mode\":[\"contain\"]}},{\"type\":\"function\",\"function\":\"render\",\"arguments\":{}}]}}]}`, + }, + { + id: 'custom-element-', + name: 'customElement3', + displayName: 'Custom Element 3', + help: + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce lobortis aliquet arcu ut turpis duis.', + image: '', + content: `{\"selectedNodes\":[{\"id\":\"group-dccf4ed7-1593-49a0-9902-caf4d4a4b7f5\",\"position\":{\"left\":250,\"top\":119,\"width\":340,\"height\":517,\"angle\":0,\"parent\":null,\"type\":\"group\"},\"expression\":\"shape fill=\\\"rgba(255,255,255,0)\\\" | render\",\"ast\":{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"shape\",\"arguments\":{\"fill\":[\"rgba(255,255,255,0)\"]}},{\"type\":\"function\",\"function\":\"render\",\"arguments\":{}}]}},{\"id\":\"element-e2c658ee-7614-4d92-a46e-2b1a81a24485\",\"position\":{\"left\":250,\"top\":405,\"width\":340,\"height\":75,\"angle\":0,\"parent\":\"group-dccf4ed7-1593-49a0-9902-caf4d4a4b7f5\",\"type\":\"element\"},\"expression\":\"filters\\n| demodata\\n| markdown \\\"## Jane Doe\\\" \\n font={font family=\\\"'Open Sans', Helvetica, Arial, sans-serif\\\" size=14 align=\\\"center\\\" color=\\\"#000000\\\" weight=\\\"normal\\\" underline=false italic=false}\\n| render\",\"ast\":{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"filters\",\"arguments\":{}},{\"type\":\"function\",\"function\":\"demodata\",\"arguments\":{}},{\"type\":\"function\",\"function\":\"markdown\",\"arguments\":{\"_\":[\"## Jane Doe\"],\"font\":[{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"font\",\"arguments\":{\"family\":[\"'Open Sans', Helvetica, Arial, sans-serif\"],\"size\":[14],\"align\":[\"center\"],\"color\":[\"#000000\"],\"weight\":[\"normal\"],\"underline\":[false],\"italic\":[false]}}]}]}},{\"type\":\"function\",\"function\":\"render\",\"arguments\":{}}]}},{\"id\":\"element-3d16765e-5251-4954-8e2a-6c64ed465b73\",\"position\":{\"left\":250,\"top\":480,\"width\":340,\"height\":75,\"angle\":0,\"parent\":\"group-dccf4ed7-1593-49a0-9902-caf4d4a4b7f5\",\"type\":\"element\"},\"expression\":\"filters\\n| demodata\\n| markdown \\\"### Developer\\\" \\n font={font family=\\\"'Open Sans', Helvetica, Arial, sans-serif\\\" size=14 align=\\\"center\\\" color=\\\"#000000\\\" weight=\\\"normal\\\" underline=false italic=false}\\n| render css=\\\".canvasRenderEl h3 {\\ncolor: #444444;\\n}\\\"\",\"ast\":{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"filters\",\"arguments\":{}},{\"type\":\"function\",\"function\":\"demodata\",\"arguments\":{}},{\"type\":\"function\",\"function\":\"markdown\",\"arguments\":{\"_\":[\"### Developer\"],\"font\":[{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"font\",\"arguments\":{\"family\":[\"'Open Sans', Helvetica, Arial, sans-serif\"],\"size\":[14],\"align\":[\"center\"],\"color\":[\"#000000\"],\"weight\":[\"normal\"],\"underline\":[false],\"italic\":[false]}}]}]}},{\"type\":\"function\",\"function\":\"render\",\"arguments\":{\"css\":[\".canvasRenderEl h3 {\\ncolor: #444444;\\n}\"]}}]}},{\"id\":\"element-624675cf-46e9-4545-b86a-5409bbe53ac1\",\"position\":{\"left\":250,\"top\":555,\"width\":340,\"height\":81,\"angle\":0,\"parent\":\"group-dccf4ed7-1593-49a0-9902-caf4d4a4b7f5\",\"type\":\"element\"},\"expression\":\"filters\\n| demodata\\n| markdown \\n \\\"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vel sollicitudin mauris, ut scelerisque urna. \\\" \\n font={font family=\\\"'Open Sans', Helvetica, Arial, sans-serif\\\" size=14 align=\\\"center\\\" color=\\\"#000000\\\" weight=\\\"normal\\\" underline=false italic=false}\\n| render\",\"ast\":{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"filters\",\"arguments\":{}},{\"type\":\"function\",\"function\":\"demodata\",\"arguments\":{}},{\"type\":\"function\",\"function\":\"markdown\",\"arguments\":{\"_\":[\"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vel sollicitudin mauris, ut scelerisque urna. \"],\"font\":[{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"font\",\"arguments\":{\"family\":[\"'Open Sans', Helvetica, Arial, sans-serif\"],\"size\":[14],\"align\":[\"center\"],\"color\":[\"#000000\"],\"weight\":[\"normal\"],\"underline\":[false],\"italic\":[false]}}]}]}},{\"type\":\"function\",\"function\":\"render\",\"arguments\":{}}]}},{\"id\":\"element-c2916246-26dd-4c65-91c6-d1ad3f1791ee\",\"position\":{\"left\":293,\"top\":119,\"width\":254,\"height\":252,\"angle\":0,\"parent\":\"group-dccf4ed7-1593-49a0-9902-caf4d4a4b7f5\",\"type\":\"element\"},\"expression\":\"image dataurl={asset \\\"asset-0c6f377f-771e-432e-8e2e-15c3e9142ad6\\\"} mode=\\\"contain\\\"\\n| render\",\"ast\":{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"image\",\"arguments\":{\"dataurl\":[{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"asset\",\"arguments\":{\"_\":[\"asset-0c6f377f-771e-432e-8e2e-15c3e9142ad6\"]}}]}],\"mode\":[\"contain\"]}},{\"type\":\"function\",\"function\":\"render\",\"arguments\":{}}]}}]}`, + }, +]; + +export const create = () => {}; + +export const get = () => {}; + +export const update = () => {}; + +export const remove = () => {}; + +export const find = () => {}; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/proportion.js b/x-pack/legacy/plugins/canvas/tasks/mocks/uiMetric.js similarity index 57% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/proportion.js rename to x-pack/legacy/plugins/canvas/tasks/mocks/uiMetric.js index 6a59a6795d45a..c7e7088812148 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/tags/proportion.js +++ b/x-pack/legacy/plugins/canvas/tasks/mocks/uiMetric.js @@ -4,7 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -import { euiPaletteColorBlind } from '@elastic/eui'; -const euiVisPalette = euiPaletteColorBlind(); - -export const proportion = () => ({ name: 'proportion', color: euiVisPalette[3] }); +export const trackCanvasUiMetric = () => {}; diff --git a/x-pack/legacy/plugins/canvas/types/canvas.ts b/x-pack/legacy/plugins/canvas/types/canvas.ts index f0137479a0b7f..0250b921aadb6 100644 --- a/x-pack/legacy/plugins/canvas/types/canvas.ts +++ b/x-pack/legacy/plugins/canvas/types/canvas.ts @@ -56,3 +56,10 @@ export type CanvasTemplate = CanvasWorkpad & { help: string; tags: string[]; }; + +export interface CanvasWorkpadBoundingBox { + left: number; + right: number; + top: number; + bottom: number; +} diff --git a/x-pack/legacy/plugins/canvas/types/elements.ts b/x-pack/legacy/plugins/canvas/types/elements.ts index acb1cb9cd7625..86356f5bd32a9 100644 --- a/x-pack/legacy/plugins/canvas/types/elements.ts +++ b/x-pack/legacy/plugins/canvas/types/elements.ts @@ -9,10 +9,10 @@ import { CanvasElement } from '.'; export interface ElementSpec { name: string; - image: string; + icon?: string; expression: string; displayName?: string; - tags?: string[]; + type?: string; help?: string; filter?: string; width?: number; @@ -42,10 +42,6 @@ export interface CustomElement { * base 64 data URL string of the preview image */ image?: string; - /** - * tags associated with the element - */ - tags?: string[]; /** * the element object stringified */ diff --git a/x-pack/plugins/canvas/server/routes/custom_elements/update.test.ts b/x-pack/plugins/canvas/server/routes/custom_elements/update.test.ts index f21a9c25b6e64..285a998030cf6 100644 --- a/x-pack/plugins/canvas/server/routes/custom_elements/update.test.ts +++ b/x-pack/plugins/canvas/server/routes/custom_elements/update.test.ts @@ -45,7 +45,6 @@ const customElement: CustomElementPayload = { name: 'MyCustomElement', displayName: 'My Wonderful Custom Element', content: 'This is content', - tags: ['filter', 'graphic'], '@created': '2019-02-08T18:35:23.029Z', '@timestamp': '2019-02-08T18:35:23.029Z', }; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index ba79ab6b9aa83..1b77dfb168e88 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -5021,8 +5021,6 @@ "xpack.canvas.elements.bubbleChartHelpText": "カスタマイズ可能なバブルチャートです", "xpack.canvas.elements.debugDisplayName": "デバッグ", "xpack.canvas.elements.debugHelpText": "エレメントの構成をダンプします", - "xpack.canvas.elements.donutChartDisplayName": "ドーナッツチャート", - "xpack.canvas.elements.donutChartHelpText": "カスタマイズ可能なドーナッツチャートです", "xpack.canvas.elements.dropdownFilterDisplayName": "ドロップダウンフィルター", "xpack.canvas.elements.dropdownFilterHelpText": "「exactly」フィルターの値を選択できるドロップダウンです", "xpack.canvas.elements.horizontalBarChartDisplayName": "水平棒グラフ", @@ -5057,8 +5055,6 @@ "xpack.canvas.elements.shapeHelpText": "カスタマイズ可能な図形です", "xpack.canvas.elements.tableDisplayName": "データテーブル", "xpack.canvas.elements.tableHelpText": "データをチューブ形式で表示する、スクロール可能なグリッドです", - "xpack.canvas.elements.tiltedPieDisplayName": "傾き円グラフ", - "xpack.canvas.elements.tiltedPieHelpText": "カスタマイズ可能な傾き円グラフです", "xpack.canvas.elements.timeFilterDisplayName": "時間フィルター", "xpack.canvas.elements.timeFilterHelpText": "期間を設定します", "xpack.canvas.elements.verticalBarChartDisplayName": "垂直棒グラフ", @@ -5069,16 +5065,16 @@ "xpack.canvas.elements.verticalProgressPillHelpText": "進捗状況を垂直のピルで表示します", "xpack.canvas.elementSettings.dataTabLabel": "データ", "xpack.canvas.elementSettings.displayTabLabel": "表示", - "xpack.canvas.elementTypes.addNewElementDescription": "ワークパッドのエレメントをグループ化して保存し、新規エレメントを作成します", - "xpack.canvas.elementTypes.addNewElementTitle": "新規エレメントの作成", - "xpack.canvas.elementTypes.cancelButtonLabel": "キャンセル", - "xpack.canvas.elementTypes.deleteButtonLabel": "削除", - "xpack.canvas.elementTypes.deleteElementDescription": "このエレメントを削除してよろしいですか?", - "xpack.canvas.elementTypes.deleteElementTitle": "エレメント「{elementName}」を削除しますか?", - "xpack.canvas.elementTypes.editElementTitle": "エレメントを編集", - "xpack.canvas.elementTypes.elementsTitle": "エレメント", - "xpack.canvas.elementTypes.findElementPlaceholder": "エレメントを検索", - "xpack.canvas.elementTypes.myElementsTitle": "マイエレメント", + "xpack.canvas.savedElementsModal.addNewElementDescription": "ワークパッドのエレメントをグループ化して保存し、新規エレメントを作成します", + "xpack.canvas.savedElementsModal.addNewElementTitle": "新規エレメントの作成", + "xpack.canvas.savedElementsModal.cancelButtonLabel": "キャンセル", + "xpack.canvas.savedElementsModal.deleteButtonLabel": "削除", + "xpack.canvas.savedElementsModal.deleteElementDescription": "このエレメントを削除してよろしいですか?", + "xpack.canvas.savedElementsModal.deleteElementTitle": "エレメント「{elementName}」を削除しますか?", + "xpack.canvas.savedElementsModal.editElementTitle": "エレメントを編集", + "xpack.canvas.savedElementsModal.elementsTitle": "エレメント", + "xpack.canvas.savedElementsModal.findElementPlaceholder": "エレメントを検索", + "xpack.canvas.savedElementsModal.myElementsTitle": "マイエレメント", "xpack.canvas.embedObject.noMatchingObjectsMessage": "一致するオブジェクトが見つかりませんでした。", "xpack.canvas.embedObject.titleText": "オブジェクトの埋め込み", "xpack.canvas.error.actionsElements.invaludArgIndexErrorMessage": "無効な引数インデックス: {index}", @@ -5591,13 +5587,8 @@ "xpack.canvas.sidebarHeader.topAlignMenuItemLabel": "一番上", "xpack.canvas.sidebarHeader.ungroupMenuItemLabel": "グループ解除", "xpack.canvas.sidebarHeader.verticalDistributionMenutItemLabel": "縦", - "xpack.canvas.tags.chartTag": "チャート", - "xpack.canvas.tags.filterTag": "フィルター", - "xpack.canvas.tags.graphicTag": "グラフィック", "xpack.canvas.tags.presentationTag": "プレゼンテーション", - "xpack.canvas.tags.proportionTag": "比率", "xpack.canvas.tags.reportTag": "レポート", - "xpack.canvas.tags.textTag": "テキスト", "xpack.canvas.templates.darkHelp": "ダークカラーテーマのプレゼンテーションデッキです", "xpack.canvas.templates.darkName": "ダーク", "xpack.canvas.templates.lightHelp": "ライトカラーテーマのプレゼンテーションデッキです", @@ -5897,7 +5888,6 @@ "xpack.canvas.workpadHeader.cycleIntervalHoursText": "{hours} {hours, plural, one {時間} other {時間}}ごと", "xpack.canvas.workpadHeader.cycleIntervalMinutesText": "{minutes} {minutes, plural, one {分} other {分}}ごと", "xpack.canvas.workpadHeader.cycleIntervalSecondsText": "{seconds} {seconds, plural, one {秒} other {秒}}ごと", - "xpack.canvas.workpadHeader.embedObjectButtonLabel": "オブジェクトを埋め込む", "xpack.canvas.workpadHeader.fullscreenButtonAriaLabel": "全画面表示", "xpack.canvas.workpadHeader.fullscreenTooltip": "全画面モードを開始します", "xpack.canvas.workpadHeader.hideEditControlTooltip": "編集コントロールを非表示にします", @@ -5907,7 +5897,6 @@ "xpack.canvas.workpadHeaderAutoRefreshControls.intervalFormLabel": "自動更新間隔を変更します", "xpack.canvas.workpadHeaderAutoRefreshControls.refreshListDurationManualText": "手動で", "xpack.canvas.workpadHeaderAutoRefreshControls.refreshListTitle": "エレメントを更新", - "xpack.canvas.workpadHeaderControlSettings.settingsTooltip": "設定をコントロールします", "xpack.canvas.workpadHeaderCustomInterval.confirmButtonLabel": "設定", "xpack.canvas.workpadHeaderCustomInterval.formDescription": "{secondsExample}、{minutesExample}、{hoursExample} のような短い表記を使用します", "xpack.canvas.workpadHeaderCustomInterval.formLabel": "カスタム間隔を設定", @@ -5916,32 +5905,32 @@ "xpack.canvas.workpadHeaderKioskControl.cycleToggleSwitch": "スライドを自動的にサイクル", "xpack.canvas.workpadHeaderRefreshControlSettings.refreshAriaLabel": "エレメントを更新", "xpack.canvas.workpadHeaderRefreshControlSettings.refreshTooltip": "データを更新", - "xpack.canvas.workpadHeaderWorkpadExport.copyPDFMessage": "{PDF} 生成 {URL} がクリップボードにコピーされました。", - "xpack.canvas.workpadHeaderWorkpadExport.copyReportingConfigMessage": "レポート構成がクリップボードにコピーされました", - "xpack.canvas.workpadHeaderWorkpadExport.copyShareConfigMessage": "共有マークアップがクリップボードにコピーされました", - "xpack.canvas.workpadHeaderWorkpadExport.exportPDFErrorMessage": "「{workpadName}」の {PDF} の作成に失敗しました", - "xpack.canvas.workpadHeaderWorkpadExport.exportPDFMessage": "{PDF} をエクスポート中です。管理で進捗を確認できます。", - "xpack.canvas.workpadHeaderWorkpadExport.exportPDFTitle": "ワークパッド「{workpadName}」の {PDF} エクスポート", - "xpack.canvas.workpadHeaderWorkpadExport.pdfPanelCopyAriaLabel": "この {URL} を使用してスクリプトから、または Watcher で {PDF} を生成することもできます。{URL} をクリップボードにコピーするにはエンターキーを押してください。", - "xpack.canvas.workpadHeaderWorkpadExport.pdfPanelCopyButtonLabel": "{POST} {URL} をコピー", - "xpack.canvas.workpadHeaderWorkpadExport.pdfPanelCopyDescription": "{POST} {URL} をコピーして {KIBANA} 外または ウォッチャー から生成することもできます。", - "xpack.canvas.workpadHeaderWorkpadExport.pdfPanelGenerateButtonLabel": "{PDF} を生成", - "xpack.canvas.workpadHeaderWorkpadExport.pdfPanelGenerateDescription": "ワークパッドのサイズによって、{PDF} の生成には数分かかる場合があります。", - "xpack.canvas.workpadHeaderWorkpadExport.shareDownloadJSONTitle": "{JSON} をダウンロード", - "xpack.canvas.workpadHeaderWorkpadExport.shareDownloadPDFTitle": "{PDF} レポート", - "xpack.canvas.workpadHeaderWorkpadExport.shareWebsiteErrorTitle": "「{workpadName}」の {ZIP} ファイルの作成に失敗しました。ワークパッドが大きすぎる可能性があります。ファイルを別々にダウンロードする必要があります。", - "xpack.canvas.workpadHeaderWorkpadExport.shareWebsiteTitle": "Web サイトで共有", - "xpack.canvas.workpadHeaderWorkpadExport.shareWorkpadMessage": "このワークパッドを共有", - "xpack.canvas.workpadHeaderWorkpadExport.unknownExportErrorMessage": "未知のエクスポートタイプ: {type}", - "xpack.canvas.workpadHeaderWorkpadExport.unsupportedRendererWarning": "このワークパッドには {CANVAS} シェアラブルワークパッドランタイムがサポートしていないレンダリング関数が含まれています。これらのエレメントはレンダリングされません:", - "xpack.canvas.workpadHeaderWorkpadZoom.zoomControlsAriaLabel": "ズームコントロール", - "xpack.canvas.workpadHeaderWorkpadZoom.zoomControlsTooltip": "ズームコントロール", - "xpack.canvas.workpadHeaderWorkpadZoom.zoomFitToWindowText": "ウィンドウに合わせる", - "xpack.canvas.workpadHeaderWorkpadZoom.zoomInText": "ズームイン", - "xpack.canvas.workpadHeaderWorkpadZoom.zoomOutText": "ズームアウト", - "xpack.canvas.workpadHeaderWorkpadZoom.zoomPanelTitle": "ズーム:", - "xpack.canvas.workpadHeaderWorkpadZoom.zoomPrecentageValue": "リセット", - "xpack.canvas.workpadHeaderWorkpadZoom.zoomResetText": "{scalePercentage}%", + "xpack.canvas.workpadHeaderShareMenu.copyPDFMessage": "{PDF} 生成 {URL} がクリップボードにコピーされました。", + "xpack.canvas.workpadHeaderShareMenu.copyReportingConfigMessage": "レポート構成がクリップボードにコピーされました", + "xpack.canvas.workpadHeaderShareMenu.copyShareConfigMessage": "共有マークアップがクリップボードにコピーされました", + "xpack.canvas.workpadHeaderShareMenu.exportPDFErrorMessage": "「{workpadName}」の {PDF} の作成に失敗しました", + "xpack.canvas.workpadHeaderShareMenu.exportPDFMessage": "{PDF} をエクスポート中です。管理で進捗を確認できます。", + "xpack.canvas.workpadHeaderShareMenu.exportPDFTitle": "ワークパッド「{workpadName}」の {PDF} エクスポート", + "xpack.canvas.workpadHeaderShareMenu.pdfPanelCopyAriaLabel": "この {URL} を使用してスクリプトから、または Watcher で {PDF} を生成することもできます。{URL} をクリップボードにコピーするにはエンターキーを押してください。", + "xpack.canvas.workpadHeaderShareMenu.pdfPanelCopyButtonLabel": "{POST} {URL} をコピー", + "xpack.canvas.workpadHeaderShareMenu.pdfPanelCopyDescription": "{POST} {URL} をコピーして {KIBANA} 外または ウォッチャー から生成することもできます。", + "xpack.canvas.workpadHeaderShareMenu.pdfPanelGenerateButtonLabel": "{PDF} を生成", + "xpack.canvas.workpadHeaderShareMenu.pdfPanelGenerateDescription": "ワークパッドのサイズによって、{PDF} の生成には数分かかる場合があります。", + "xpack.canvas.workpadHeaderShareMenu.shareDownloadJSONTitle": "{JSON} をダウンロード", + "xpack.canvas.workpadHeaderShareMenu.shareDownloadPDFTitle": "{PDF} レポート", + "xpack.canvas.workpadHeaderShareMenu.shareWebsiteErrorTitle": "「{workpadName}」の {ZIP} ファイルの作成に失敗しました。ワークパッドが大きすぎる可能性があります。ファイルを別々にダウンロードする必要があります。", + "xpack.canvas.workpadHeaderShareMenu.shareWebsiteTitle": "Web サイトで共有", + "xpack.canvas.workpadHeaderShareMenu.shareWorkpadMessage": "このワークパッドを共有", + "xpack.canvas.workpadHeaderShareMenu.unknownExportErrorMessage": "未知のエクスポートタイプ: {type}", + "xpack.canvas.workpadHeaderShareMenu.unsupportedRendererWarning": "このワークパッドには {CANVAS} シェアラブルワークパッドランタイムがサポートしていないレンダリング関数が含まれています。これらのエレメントはレンダリングされません:", + "xpack.canvas.workpadHeaderViewMenu.zoomControlsAriaLabel": "ズームコントロール", + "xpack.canvas.workpadHeaderViewMenu.zoomControlsTooltip": "ズームコントロール", + "xpack.canvas.workpadHeaderViewMenu.zoomFitToWindowText": "ウィンドウに合わせる", + "xpack.canvas.workpadHeaderViewMenu.zoomInText": "ズームイン", + "xpack.canvas.workpadHeaderViewMenu.zoomOutText": "ズームアウト", + "xpack.canvas.workpadHeaderViewMenu.zoomPanelTitle": "ズーム:", + "xpack.canvas.workpadHeaderViewMenu.zoomPrecentageValue": "リセット", + "xpack.canvas.workpadHeaderViewMenu.zoomResetText": "{scalePercentage}%", "xpack.canvas.workpadLoader.clonedWorkpadName": "{workpadName} のコピー", "xpack.canvas.workpadLoader.cloneTooltip": "ワークパッドのクローンを作成します", "xpack.canvas.workpadLoader.createWorkpadLoadingDescription": "ワークパッドを作成中...", @@ -10910,7 +10899,6 @@ "xpack.monitoring.alerts.licenseExpiration.actionGroups.default": "デフォルト", "xpack.monitoring.alerts.licenseExpiration.newSubject": "NEW X-Pack 監視:ライセンス期限", "xpack.monitoring.alerts.licenseExpiration.resolvedSubject": "RESOLVED X-Pack 監視:ライセンス期限", - "xpack.monitoring.alerts.licenseExpiration.ui.firingMessage": "このクラスターのライセンスは、#relative で #absolute に期限が切れます。", "xpack.monitoring.alerts.licenseExpiration.ui.resolvedMessage": "このクラスターのライセンスはアクティブです。", "xpack.monitoring.alerts.lowSeverityName": "低", "xpack.monitoring.alerts.mediumSeverityName": "中", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index a8d785f47144c..ce2469f29b883 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -5022,8 +5022,6 @@ "xpack.canvas.elements.bubbleChartHelpText": "可定制的气泡图", "xpack.canvas.elements.debugDisplayName": "“Debug”(故障排查)", "xpack.canvas.elements.debugHelpText": "只需丢弃元素的配置", - "xpack.canvas.elements.donutChartDisplayName": "圆环图", - "xpack.canvas.elements.donutChartHelpText": "可定制的圆环图", "xpack.canvas.elements.dropdownFilterDisplayName": "下拉列表筛选", "xpack.canvas.elements.dropdownFilterHelpText": "可以从其中为“完全”筛选选择值的下拉列表", "xpack.canvas.elements.horizontalBarChartDisplayName": "水平条形图", @@ -5058,8 +5056,6 @@ "xpack.canvas.elements.shapeHelpText": "可定制的形状", "xpack.canvas.elements.tableDisplayName": "数据表", "xpack.canvas.elements.tableHelpText": "用于以表格形式显示数据的可滚动网格", - "xpack.canvas.elements.tiltedPieDisplayName": "斜饼图", - "xpack.canvas.elements.tiltedPieHelpText": "可定制的斜饼图", "xpack.canvas.elements.timeFilterDisplayName": "时间筛选", "xpack.canvas.elements.timeFilterHelpText": "设置时间窗口", "xpack.canvas.elements.verticalBarChartDisplayName": "垂直条形图", @@ -5070,16 +5066,16 @@ "xpack.canvas.elements.verticalProgressPillHelpText": "将进度显示为垂直胶囊的一部分", "xpack.canvas.elementSettings.dataTabLabel": "数据", "xpack.canvas.elementSettings.displayTabLabel": "显示", - "xpack.canvas.elementTypes.addNewElementDescription": "分组并保存 Workpad 元素以创建新元素", - "xpack.canvas.elementTypes.addNewElementTitle": "添加新元素", - "xpack.canvas.elementTypes.cancelButtonLabel": "取消", - "xpack.canvas.elementTypes.deleteButtonLabel": "删除", - "xpack.canvas.elementTypes.deleteElementDescription": "确定要删除此元素?", - "xpack.canvas.elementTypes.deleteElementTitle": "删除元素“{elementName}”?", - "xpack.canvas.elementTypes.editElementTitle": "编辑元素", - "xpack.canvas.elementTypes.elementsTitle": "元素", - "xpack.canvas.elementTypes.findElementPlaceholder": "查找元素", - "xpack.canvas.elementTypes.myElementsTitle": "我的元素", + "xpack.canvas.savedElementsModal.addNewElementDescription": "分组并保存 Workpad 元素以创建新元素", + "xpack.canvas.savedElementsModal.addNewElementTitle": "添加新元素", + "xpack.canvas.savedElementsModal.cancelButtonLabel": "取消", + "xpack.canvas.savedElementsModal.deleteButtonLabel": "删除", + "xpack.canvas.savedElementsModal.deleteElementDescription": "确定要删除此元素?", + "xpack.canvas.savedElementsModal.deleteElementTitle": "删除元素“{elementName}”?", + "xpack.canvas.savedElementsModal.editElementTitle": "编辑元素", + "xpack.canvas.savedElementsModal.elementsTitle": "元素", + "xpack.canvas.savedElementsModal.findElementPlaceholder": "查找元素", + "xpack.canvas.savedElementsModal.myElementsTitle": "我的元素", "xpack.canvas.embedObject.noMatchingObjectsMessage": "未找到任何匹配对象。", "xpack.canvas.embedObject.titleText": "嵌入对象", "xpack.canvas.error.actionsElements.invaludArgIndexErrorMessage": "无效的参数索引:{index}", @@ -5592,13 +5588,8 @@ "xpack.canvas.sidebarHeader.topAlignMenuItemLabel": "上", "xpack.canvas.sidebarHeader.ungroupMenuItemLabel": "取消分组", "xpack.canvas.sidebarHeader.verticalDistributionMenutItemLabel": "垂直", - "xpack.canvas.tags.chartTag": "图表", - "xpack.canvas.tags.filterTag": "筛选", - "xpack.canvas.tags.graphicTag": "图形", "xpack.canvas.tags.presentationTag": "演示", - "xpack.canvas.tags.proportionTag": "比例", "xpack.canvas.tags.reportTag": "报告", - "xpack.canvas.tags.textTag": "文本", "xpack.canvas.templates.darkHelp": "深色主题的演示幻灯片", "xpack.canvas.templates.darkName": "深色", "xpack.canvas.templates.lightHelp": "浅色主题的演示幻灯片", @@ -5899,7 +5890,6 @@ "xpack.canvas.workpadHeader.cycleIntervalHoursText": "每 {hours} {hours, plural, one {小时} other {小时}}", "xpack.canvas.workpadHeader.cycleIntervalMinutesText": "每 {minutes} {minutes, plural, one {分钟} other {分钟}}", "xpack.canvas.workpadHeader.cycleIntervalSecondsText": "每 {seconds} {seconds, plural, one {秒} other {秒}}", - "xpack.canvas.workpadHeader.embedObjectButtonLabel": "嵌入对象", "xpack.canvas.workpadHeader.fullscreenButtonAriaLabel": "全屏查看", "xpack.canvas.workpadHeader.fullscreenTooltip": "进入全屏模式", "xpack.canvas.workpadHeader.hideEditControlTooltip": "隐藏编辑控件", @@ -5909,7 +5899,6 @@ "xpack.canvas.workpadHeaderAutoRefreshControls.intervalFormLabel": "更改自动刷新时间间隔", "xpack.canvas.workpadHeaderAutoRefreshControls.refreshListDurationManualText": "手动", "xpack.canvas.workpadHeaderAutoRefreshControls.refreshListTitle": "刷新元素", - "xpack.canvas.workpadHeaderControlSettings.settingsTooltip": "控制设置", "xpack.canvas.workpadHeaderCustomInterval.confirmButtonLabel": "设置", "xpack.canvas.workpadHeaderCustomInterval.formDescription": "使用速记表示法,如 {secondsExample}、{minutesExample} 或 {hoursExample}", "xpack.canvas.workpadHeaderCustomInterval.formLabel": "设置定制时间间隔", @@ -5918,32 +5907,32 @@ "xpack.canvas.workpadHeaderKioskControl.cycleToggleSwitch": "自动循环播放幻灯片", "xpack.canvas.workpadHeaderRefreshControlSettings.refreshAriaLabel": "刷新元素", "xpack.canvas.workpadHeaderRefreshControlSettings.refreshTooltip": "刷新数据", - "xpack.canvas.workpadHeaderWorkpadExport.copyPDFMessage": "{PDF} 生成 {URL} 已复制到剪贴板", - "xpack.canvas.workpadHeaderWorkpadExport.copyReportingConfigMessage": "已将报告配置复制到剪贴板", - "xpack.canvas.workpadHeaderWorkpadExport.copyShareConfigMessage": "已将共享标记复制到剪贴板", - "xpack.canvas.workpadHeaderWorkpadExport.exportPDFErrorMessage": "无法为“{workpadName}”创建 {PDF}", - "xpack.canvas.workpadHeaderWorkpadExport.exportPDFMessage": "正在导出 {PDF}。可以在“管理”中跟踪进度。", - "xpack.canvas.workpadHeaderWorkpadExport.exportPDFTitle": "Workpad“{workpadName}”的 {PDF} 导出", - "xpack.canvas.workpadHeaderWorkpadExport.pdfPanelCopyAriaLabel": "或者,也可以从脚本或使用 {URL} 通过 Watcher 生成 {PDF}。按 Enter 键可将 {URL} 复制到剪贴板。", - "xpack.canvas.workpadHeaderWorkpadExport.pdfPanelCopyButtonLabel": "复制 {POST} {URL}", - "xpack.canvas.workpadHeaderWorkpadExport.pdfPanelCopyDescription": "或者,复制此 {POST} {URL} 以从 {KIBANA} 外部或从 Watcher 调用生成。", - "xpack.canvas.workpadHeaderWorkpadExport.pdfPanelGenerateButtonLabel": "生成 {PDF}", - "xpack.canvas.workpadHeaderWorkpadExport.pdfPanelGenerateDescription": "{PDF} 可能会花费 1 或 2 分钟生成,取决于 Workpad 的大小。", - "xpack.canvas.workpadHeaderWorkpadExport.shareDownloadJSONTitle": "下载为 {JSON}", - "xpack.canvas.workpadHeaderWorkpadExport.shareDownloadPDFTitle": "{PDF} 报告", - "xpack.canvas.workpadHeaderWorkpadExport.shareWebsiteErrorTitle": "无法为“{workpadName}”创建 {ZIP} 文件。Workpad 可能过大。您将需要分别下载文件。", - "xpack.canvas.workpadHeaderWorkpadExport.shareWebsiteTitle": "在网站上共享", - "xpack.canvas.workpadHeaderWorkpadExport.shareWorkpadMessage": "共享此 Workpad", - "xpack.canvas.workpadHeaderWorkpadExport.unknownExportErrorMessage": "未知导出类型:{type}", - "xpack.canvas.workpadHeaderWorkpadExport.unsupportedRendererWarning": "此 Workpad 包含 {CANVAS} Shareable Workpad Runtime 不支持的呈现函数。将不会呈现以下元素:", - "xpack.canvas.workpadHeaderWorkpadZoom.zoomControlsAriaLabel": "缩放控制", - "xpack.canvas.workpadHeaderWorkpadZoom.zoomControlsTooltip": "缩放控制", - "xpack.canvas.workpadHeaderWorkpadZoom.zoomFitToWindowText": "适应窗口大小", - "xpack.canvas.workpadHeaderWorkpadZoom.zoomInText": "放大", - "xpack.canvas.workpadHeaderWorkpadZoom.zoomOutText": "缩小", - "xpack.canvas.workpadHeaderWorkpadZoom.zoomPanelTitle": "缩放", - "xpack.canvas.workpadHeaderWorkpadZoom.zoomPrecentageValue": "重置", - "xpack.canvas.workpadHeaderWorkpadZoom.zoomResetText": "{scalePercentage}%", + "xpack.canvas.workpadHeaderShareMenu.copyPDFMessage": "{PDF} 生成 {URL} 已复制到剪贴板", + "xpack.canvas.workpadHeaderShareMenu.copyReportingConfigMessage": "已将报告配置复制到剪贴板", + "xpack.canvas.workpadHeaderShareMenu.copyShareConfigMessage": "已将共享标记复制到剪贴板", + "xpack.canvas.workpadHeaderShareMenu.exportPDFErrorMessage": "无法为“{workpadName}”创建 {PDF}", + "xpack.canvas.workpadHeaderShareMenu.exportPDFMessage": "正在导出 {PDF}。可以在“管理”中跟踪进度。", + "xpack.canvas.workpadHeaderShareMenu.exportPDFTitle": "Workpad“{workpadName}”的 {PDF} 导出", + "xpack.canvas.workpadHeaderShareMenu.pdfPanelCopyAriaLabel": "或者,也可以从脚本或使用 {URL} 通过 Watcher 生成 {PDF}。按 Enter 键可将 {URL} 复制到剪贴板。", + "xpack.canvas.workpadHeaderShareMenu.pdfPanelCopyButtonLabel": "复制 {POST} {URL}", + "xpack.canvas.workpadHeaderShareMenu.pdfPanelCopyDescription": "或者,复制此 {POST} {URL} 以从 {KIBANA} 外部或从 Watcher 调用生成。", + "xpack.canvas.workpadHeaderShareMenu.pdfPanelGenerateButtonLabel": "生成 {PDF}", + "xpack.canvas.workpadHeaderShareMenu.pdfPanelGenerateDescription": "{PDF} 可能会花费 1 或 2 分钟生成,取决于 Workpad 的大小。", + "xpack.canvas.workpadHeaderShareMenu.shareDownloadJSONTitle": "下载为 {JSON}", + "xpack.canvas.workpadHeaderShareMenu.shareDownloadPDFTitle": "{PDF} 报告", + "xpack.canvas.workpadHeaderShareMenu.shareWebsiteErrorTitle": "无法为“{workpadName}”创建 {ZIP} 文件。Workpad 可能过大。您将需要分别下载文件。", + "xpack.canvas.workpadHeaderShareMenu.shareWebsiteTitle": "在网站上共享", + "xpack.canvas.workpadHeaderShareMenu.shareWorkpadMessage": "共享此 Workpad", + "xpack.canvas.workpadHeaderShareMenu.unknownExportErrorMessage": "未知导出类型:{type}", + "xpack.canvas.workpadHeaderShareMenu.unsupportedRendererWarning": "此 Workpad 包含 {CANVAS} Shareable Workpad Runtime 不支持的呈现函数。将不会呈现以下元素:", + "xpack.canvas.workpadHeaderViewMenu.zoomControlsAriaLabel": "缩放控制", + "xpack.canvas.workpadHeaderViewMenu.zoomControlsTooltip": "缩放控制", + "xpack.canvas.workpadHeaderViewMenu.zoomFitToWindowText": "适应窗口大小", + "xpack.canvas.workpadHeaderViewMenu.zoomInText": "放大", + "xpack.canvas.workpadHeaderViewMenu.zoomOutText": "缩小", + "xpack.canvas.workpadHeaderViewMenu.zoomPanelTitle": "缩放", + "xpack.canvas.workpadHeaderViewMenu.zoomPrecentageValue": "重置", + "xpack.canvas.workpadHeaderViewMenu.zoomResetText": "{scalePercentage}%", "xpack.canvas.workpadLoader.clonedWorkpadName": "{workpadName} 的副本", "xpack.canvas.workpadLoader.cloneTooltip": "克隆 Workpad", "xpack.canvas.workpadLoader.createWorkpadLoadingDescription": "正在创建 Workpad......", @@ -10914,7 +10903,6 @@ "xpack.monitoring.alerts.licenseExpiration.actionGroups.default": "默认值", "xpack.monitoring.alerts.licenseExpiration.newSubject": "新 X-Pack Monitoring:许可证到期", "xpack.monitoring.alerts.licenseExpiration.resolvedSubject": "已解决 X-Pack Monitoring:许可证到期", - "xpack.monitoring.alerts.licenseExpiration.ui.firingMessage": "此集群的许可证将在 #relative 后,即 #absolute到期", "xpack.monitoring.alerts.licenseExpiration.ui.resolvedMessage": "此集群的许可证处于活动状态。", "xpack.monitoring.alerts.lowSeverityName": "低", "xpack.monitoring.alerts.mediumSeverityName": "中", diff --git a/x-pack/test/functional/apps/canvas/custom_elements.ts b/x-pack/test/functional/apps/canvas/custom_elements.ts index de3976509be1f..d4e1702368879 100644 --- a/x-pack/test/functional/apps/canvas/custom_elements.ts +++ b/x-pack/test/functional/apps/canvas/custom_elements.ts @@ -19,8 +19,7 @@ export default function canvasCustomElementTest({ const PageObjects = getPageObjects(['canvas', 'common']); const find = getService('find'); - // FLAKY: https://github.com/elastic/kibana/issues/62927 - describe.skip('custom elements', function() { + describe('custom elements', function() { this.tags('skipFirefox'); before(async () => { @@ -42,7 +41,7 @@ export default function canvasCustomElementTest({ await testSubjects.click('canvasWorkpadPage > canvasWorkpadPageElementContent', 20000); // click the "Save as new element" button - await find.clickByCssSelector('[aria-label="Save as new element"]', 20000); + await testSubjects.click('canvasSidebarHeader__saveElementButton', 20000); // fill out the custom element form and submit it await PageObjects.canvas.fillOutCustomElementForm( @@ -57,15 +56,11 @@ export default function canvasCustomElementTest({ }); it('adds the custom element to the workpad when prompted', async () => { - await PageObjects.canvas.openAddElementModal(); - - // open the custom elements tab - await find.clickByCssSelector('#customElements', 20000); + // open the saved elements modal + await PageObjects.canvas.openSavedElementsModal(); // ensure the custom element is the one expected and click it to add to the workpad - const customElement = await find.byCssSelector( - '[aria-labelledby="customElements"] .canvasElementCard__wrapper' - ); + const customElement = await find.byCssSelector('.canvasElementCard__wrapper'); const elementName = await customElement.findByCssSelector('.euiCard__title'); expect(await elementName.getVisibleText()).to.contain('My New Element'); customElement.click(); @@ -95,14 +90,11 @@ export default function canvasCustomElementTest({ }); it('saves custom element modifications', async () => { - await PageObjects.canvas.openAddElementModal(); - - // open the custom elements tab - await find.clickByCssSelector('#customElements', 20000); + // open the saved elements modal + await PageObjects.canvas.openSavedElementsModal(); // ensure the correct amount of custom elements exist - const container = await find.byCssSelector('[aria-labelledby="customElements"]'); - const customElements = await container.findAllByCssSelector('.canvasElementCard__wrapper'); + const customElements = await find.allByCssSelector('.canvasElementCard__wrapper'); expect(customElements).to.have.length(1); // hover over the custom element to bring up the edit and delete icons @@ -110,8 +102,7 @@ export default function canvasCustomElementTest({ await customElement.moveMouseTo(); // click the edit element button - const editBtn = await customElement.findByCssSelector('[aria-label="Edit element"]'); - await editBtn.click(); + await testSubjects.click('canvasElementCard__editButton', 20000); // fill out the custom element form and submit it await PageObjects.canvas.fillOutCustomElementForm( @@ -121,22 +112,21 @@ export default function canvasCustomElementTest({ // ensure the custom element in the modal shows the updated text await retry.try(async () => { - const elementName = await find.byCssSelector( - '[aria-labelledby="customElements"] .canvasElementCard__wrapper .euiCard__title' - ); + const elementName = await find.byCssSelector('.canvasElementCard__wrapper .euiCard__title'); expect(await elementName.getVisibleText()).to.contain('My Edited New Element'); }); + + // Close the modal + await PageObjects.canvas.closeSavedElementsModal(); }); it('deletes custom element when prompted', async () => { - // open the custom elements tab - await find.clickByCssSelector('#customElements', 20000); + // open the saved elements modal + await PageObjects.canvas.openSavedElementsModal(); // ensure the correct amount of custom elements exist - const customElements = await find.allByCssSelector( - '[aria-labelledby="customElements"] .canvasElementCard__wrapper' - ); + const customElements = await find.allByCssSelector('.canvasElementCard__wrapper'); expect(customElements).to.have.length(1); // hover over the custom element to bring up the edit and delete icons @@ -144,22 +134,18 @@ export default function canvasCustomElementTest({ await customElement.moveMouseTo(); // click the delete element button - const editBtn = await customElement.findByCssSelector('[aria-label="Delete element"]'); - await editBtn.click(); + await testSubjects.click('canvasElementCard__deleteButton', 20000); - await testSubjects.click('confirmModalConfirmButton'); + await testSubjects.click('confirmModalConfirmButton', 20000); // ensure the custom element was deleted await retry.try(async () => { - const containerAgain = await find.byCssSelector('[aria-labelledby="customElements"]'); - const customElementsAgain = await containerAgain.findAllByCssSelector( - '.canvasElementCard__wrapper' - ); + const customElementsAgain = await find.allByCssSelector('.canvasElementCard__wrapper'); expect(customElementsAgain).to.have.length(0); }); // Close the modal - await browser.pressKeys(browser.keys.ESCAPE); + await PageObjects.canvas.closeSavedElementsModal(); }); }); } diff --git a/x-pack/test/functional/page_objects/canvas_page.ts b/x-pack/test/functional/page_objects/canvas_page.ts index de826097a5be6..94ad393ead3a3 100644 --- a/x-pack/test/functional/page_objects/canvas_page.ts +++ b/x-pack/test/functional/page_objects/canvas_page.ts @@ -51,8 +51,12 @@ export function CanvasPageProvider({ getService }: FtrProviderContext) { expect(disabledAttr).to.be('true'); }, - async openAddElementModal() { + async openSavedElementsModal() { await testSubjects.click('add-element-button'); + await testSubjects.click('saved-elements-menu-option'); + }, + async closeSavedElementsModal() { + await testSubjects.click('saved-elements-modal-close-button'); }, async expectAddElementButton() { From 58d56884c2789ddabdc5144eab0b519f856bb474 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 23 Apr 2020 09:44:49 +0200 Subject: [PATCH 18/39] fix plugin id (#64166) --- src/plugins/home/kibana.json | 2 +- src/plugins/home/server/plugin.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/home/kibana.json b/src/plugins/home/kibana.json index d5b047924f599..1c4b44a946e62 100644 --- a/src/plugins/home/kibana.json +++ b/src/plugins/home/kibana.json @@ -4,5 +4,5 @@ "server": true, "ui": true, "requiredPlugins": ["data", "kibanaLegacy"], - "optionalPlugins": ["usage_collection", "telemetry"] + "optionalPlugins": ["usageCollection", "telemetry"] } diff --git a/src/plugins/home/server/plugin.ts b/src/plugins/home/server/plugin.ts index 6ea6784cd5adb..1f530bc58f0b9 100644 --- a/src/plugins/home/server/plugin.ts +++ b/src/plugins/home/server/plugin.ts @@ -29,7 +29,7 @@ import { UsageCollectionSetup } from '../../usage_collection/server'; import { sampleDataTelemetry } from './saved_objects'; interface HomeServerPluginSetupDependencies { - usage_collection?: UsageCollectionSetup; + usageCollection?: UsageCollectionSetup; } export class HomeServerPlugin implements Plugin { @@ -41,7 +41,7 @@ export class HomeServerPlugin implements Plugin Date: Thu, 23 Apr 2020 10:10:15 +0100 Subject: [PATCH 19/39] [Task Manager] improves cancelation messaging in Task Manager (#64075) Instead of warning that a task isn't cancellable despite having been cancelled, we now do so as a debug warning. We now warn when a task has expired and is about to be cancelled including when it expired and how long it ran for. --- .../task_manager/server/task_pool.test.ts | 33 +++++++++- .../plugins/task_manager/server/task_pool.ts | 21 ++++++- .../task_manager/server/task_runner.test.ts | 62 +++++++++++++++++-- .../task_manager/server/task_runner.ts | 23 ++++++- 4 files changed, 129 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/task_manager/server/task_pool.test.ts b/x-pack/plugins/task_manager/server/task_pool.test.ts index fb87b6290a3da..dee4d80b97917 100644 --- a/x-pack/plugins/task_manager/server/task_pool.test.ts +++ b/x-pack/plugins/task_manager/server/task_pool.test.ts @@ -9,6 +9,7 @@ import { TaskPool, TaskPoolRunResult } from './task_pool'; import { mockLogger, resolvable, sleep } from './test_utils'; import { asOk } from './lib/result_type'; import { SavedObjectsErrorHelpers } from '../../../../src/core/server'; +import moment from 'moment'; describe('TaskPool', () => { test('occupiedWorkers are a sum of running tasks', async () => { @@ -190,14 +191,16 @@ describe('TaskPool', () => { }); test('run cancels expired tasks prior to running new tasks', async () => { + const logger = mockLogger(); const pool = new TaskPool({ maxWorkers: 2, - logger: mockLogger(), + logger, }); const expired = resolvable(); const shouldRun = sinon.spy(() => Promise.resolve()); const shouldNotRun = sinon.spy(() => Promise.resolve()); + const now = new Date(); const result = await pool.run([ { ...mockTask(), @@ -207,6 +210,16 @@ describe('TaskPool', () => { await sleep(10); return asOk({ state: {} }); }, + get expiration() { + return now; + }, + get startedAt() { + // 5 and a half minutes + return moment(now) + .subtract(5, 'm') + .subtract(30, 's') + .toDate(); + }, cancel: shouldRun, }, { @@ -231,6 +244,10 @@ describe('TaskPool', () => { expect(pool.occupiedWorkers).toEqual(2); expect(pool.availableWorkers).toEqual(0); + + expect(logger.warn).toHaveBeenCalledWith( + `Cancelling task TaskType "shooooo" as it expired at ${now.toISOString()} after running for 05m 30s (with timeout set at 5m).` + ); }); test('logs if cancellation errors', async () => { @@ -285,6 +302,20 @@ describe('TaskPool', () => { markTaskAsRunning: jest.fn(async () => true), run: mockRun(), toString: () => `TaskType "shooooo"`, + get expiration() { + return new Date(); + }, + get startedAt() { + return new Date(); + }, + get definition() { + return { + type: '', + title: '', + timeout: '5m', + createTaskRunner: jest.fn(), + }; + }, }; } }); diff --git a/x-pack/plugins/task_manager/server/task_pool.ts b/x-pack/plugins/task_manager/server/task_pool.ts index 8999fb48680ce..bd0de86551aaa 100644 --- a/x-pack/plugins/task_manager/server/task_pool.ts +++ b/x-pack/plugins/task_manager/server/task_pool.ts @@ -8,7 +8,9 @@ * This module contains the logic that ensures we don't run too many * tasks at once in a given Kibana instance. */ +import moment, { Duration } from 'moment'; import { performance } from 'perf_hooks'; +import { padLeft } from 'lodash'; import { Logger } from './types'; import { TaskRunner } from './task_runner'; import { isTaskSavedObjectNotFoundError } from './lib/is_task_not_found_error'; @@ -148,7 +150,19 @@ export class TaskPool { private cancelExpiredTasks() { for (const task of this.running) { if (task.isExpired) { - this.logger.debug(`Cancelling expired task ${task.toString()}.`); + this.logger.warn( + `Cancelling task ${task.toString()} as it expired at ${task.expiration.toISOString()}${ + task.startedAt + ? ` after running for ${durationAsString( + moment.duration( + moment(new Date()) + .utc() + .diff(task.startedAt) + ) + )}` + : `` + }${task.definition.timeout ? ` (with timeout set at ${task.definition.timeout})` : ``}.` + ); this.cancelTask(task); } } @@ -169,3 +183,8 @@ function partitionListByCount(list: T[], count: number): [T[], T[]] { const listInCount = list.splice(0, count); return [listInCount, list]; } + +function durationAsString(duration: Duration): string { + const [m, s] = [duration.minutes(), duration.seconds()].map(value => padLeft(`${value}`, 2, '0')); + return `${m}m ${s}s`; +} diff --git a/x-pack/plugins/task_manager/server/task_runner.test.ts b/x-pack/plugins/task_manager/server/task_runner.test.ts index 3f0132105347e..fad3bf96905ae 100644 --- a/x-pack/plugins/task_manager/server/task_runner.test.ts +++ b/x-pack/plugins/task_manager/server/task_runner.test.ts @@ -13,6 +13,7 @@ import { ConcreteTaskInstance, TaskStatus } from './task'; import { TaskManagerRunner } from './task_runner'; import { mockLogger } from './test_utils'; import { SavedObjectsErrorHelpers } from '../../../../src/core/server'; +import moment from 'moment'; let fakeTimer: sinon.SinonFakeTimers; @@ -113,6 +114,60 @@ describe('TaskManagerRunner', () => { expect(instance.runAt.getTime()).toBeLessThanOrEqual(minutesFromNow(10).getTime()); }); + test('expiration returns time after which timeout will have elapsed from start', async () => { + const now = moment(); + const { runner } = testOpts({ + instance: { + schedule: { interval: '10m' }, + status: TaskStatus.Running, + startedAt: now.toDate(), + }, + definitions: { + bar: { + timeout: `1m`, + createTaskRunner: () => ({ + async run() { + return; + }, + }), + }, + }, + }); + + await runner.run(); + + expect(runner.isExpired).toBe(false); + expect(runner.expiration).toEqual(now.add(1, 'm').toDate()); + }); + + test('runDuration returns duration which has elapsed since start', async () => { + const now = moment() + .subtract(30, 's') + .toDate(); + const { runner } = testOpts({ + instance: { + schedule: { interval: '10m' }, + status: TaskStatus.Running, + startedAt: now, + }, + definitions: { + bar: { + timeout: `1m`, + createTaskRunner: () => ({ + async run() { + return; + }, + }), + }, + }, + }); + + await runner.run(); + + expect(runner.isExpired).toBe(false); + expect(runner.startedAt).toEqual(now); + }); + test('reschedules tasks that return a runAt', async () => { const runAt = minutesFromNow(_.random(1, 10)); const { runner, store } = testOpts({ @@ -208,7 +263,7 @@ describe('TaskManagerRunner', () => { expect(logger.warn).not.toHaveBeenCalled(); }); - test('warns if cancel is called on a non-cancellable task', async () => { + test('debug logs if cancel is called on a non-cancellable task', async () => { const { runner, logger } = testOpts({ definitions: { bar: { @@ -223,10 +278,7 @@ describe('TaskManagerRunner', () => { await runner.cancel(); await promise; - expect(logger.warn).toHaveBeenCalledTimes(1); - expect(logger.warn.mock.calls[0][0]).toMatchInlineSnapshot( - `"The task bar \\"foo\\" is not cancellable."` - ); + expect(logger.debug).toHaveBeenCalledWith(`The task bar "foo" is not cancellable.`); }); test('sets startedAt, status, attempts and retryAt when claiming a task', async () => { diff --git a/x-pack/plugins/task_manager/server/task_runner.ts b/x-pack/plugins/task_manager/server/task_runner.ts index 682885aaa0b1c..ec1c40dc80731 100644 --- a/x-pack/plugins/task_manager/server/task_runner.ts +++ b/x-pack/plugins/task_manager/server/task_runner.ts @@ -39,6 +39,9 @@ const EMPTY_RUN_RESULT: SuccessfulRunResult = {}; export interface TaskRunner { isExpired: boolean; + expiration: Date; + startedAt: Date | null; + definition: TaskDefinition; cancel: CancelFunction; markTaskAsRunning: () => Promise; run: () => Promise>; @@ -129,11 +132,25 @@ export class TaskManagerRunner implements TaskRunner { return this.definitions[this.taskType]; } + /** + * Gets the time at which this task will expire. + */ + public get expiration() { + return intervalFromDate(this.instance.startedAt!, this.definition.timeout)!; + } + + /** + * Gets the duration of the current task run + */ + public get startedAt() { + return this.instance.startedAt; + } + /** * Gets whether or not this task has run longer than its expiration setting allows. */ public get isExpired() { - return intervalFromDate(this.instance.startedAt!, this.definition.timeout)! < new Date(); + return this.expiration < new Date(); } /** @@ -261,12 +278,12 @@ export class TaskManagerRunner implements TaskRunner { */ public async cancel() { const { task } = this; - if (task && task.cancel) { + if (task?.cancel) { this.task = undefined; return task.cancel(); } - this.logger.warn(`The task ${this} is not cancellable.`); + this.logger.debug(`The task ${this} is not cancellable.`); } private validateResult(result?: RunResult | void): Result { From 7045b02b36c3b2359eeb8e9ca2af96f9fe16dd19 Mon Sep 17 00:00:00 2001 From: Diana Derevyankina <54894989+DziyanaDzeraviankina@users.noreply.github.com> Date: Thu, 23 Apr 2020 12:30:00 +0300 Subject: [PATCH 20/39] [NP] Migrate Tagcloud to NP (#63445) * [NP] Migrate Tagcloud to NP * Fixed eslint issue * Added styles import and disabled eslint in some lines with paths * Added a simple server part and moved tests * Imported setFormatService to fix initialization * Fixed import of setFormatService * Removed unnecessary eslint disable comment --- .i18nrc.json | 2 +- .../vis_type_tagcloud}/afterparamchange.png | Bin .../vis_type_tagcloud}/afterresize.png | Bin .../vis_type_tagcloud}/basicdraw.png | Bin .../vis_type_tagcloud}/simpleload.png | Bin .../__tests__/vis_type_tagcloud}/tag_cloud.js | 4 +- .../tag_cloud_visualization.js | 12 ++++- .../core_plugins/vis_type_tagcloud/index.ts | 44 ------------------ .../vis_type_tagcloud/package.json | 4 -- src/plugins/vis_type_tagcloud/config.ts | 26 +++++++++++ src/plugins/vis_type_tagcloud/kibana.json | 7 +++ .../__snapshots__/tag_cloud_fn.test.ts.snap | 0 .../vis_type_tagcloud/public/_tag_cloud.scss | 0 .../public/components/feedback_message.js | 0 .../public/components/label.js | 0 .../public/components/tag_cloud.js | 0 .../public/components/tag_cloud_options.tsx | 6 +-- .../components/tag_cloud_visualization.js | 0 .../vis_type_tagcloud/public/index.scss | 2 - .../vis_type_tagcloud/public/index.ts | 2 +- .../vis_type_tagcloud/public/plugin.ts | 17 ++++--- .../vis_type_tagcloud/public/services.ts | 6 +-- .../public/tag_cloud_fn.test.ts | 3 +- .../vis_type_tagcloud/public/tag_cloud_fn.ts | 6 +-- .../public/tag_cloud_type.ts | 2 +- .../vis_type_tagcloud/public/types.ts | 0 .../vis_type_tagcloud/server/index.ts} | 24 +++++----- 27 files changed, 78 insertions(+), 89 deletions(-) rename src/legacy/core_plugins/{vis_type_tagcloud/public/components/__tests__ => kibana/public/__tests__/vis_type_tagcloud}/afterparamchange.png (100%) rename src/legacy/core_plugins/{vis_type_tagcloud/public/components/__tests__ => kibana/public/__tests__/vis_type_tagcloud}/afterresize.png (100%) rename src/legacy/core_plugins/{vis_type_tagcloud/public/components/__tests__ => kibana/public/__tests__/vis_type_tagcloud}/basicdraw.png (100%) rename src/legacy/core_plugins/{vis_type_tagcloud/public/components/__tests__ => kibana/public/__tests__/vis_type_tagcloud}/simpleload.png (100%) rename src/legacy/core_plugins/{vis_type_tagcloud/public/components/__tests__ => kibana/public/__tests__/vis_type_tagcloud}/tag_cloud.js (98%) rename src/legacy/core_plugins/{vis_type_tagcloud/public/components/__tests__ => kibana/public/__tests__/vis_type_tagcloud}/tag_cloud_visualization.js (89%) delete mode 100644 src/legacy/core_plugins/vis_type_tagcloud/index.ts delete mode 100644 src/legacy/core_plugins/vis_type_tagcloud/package.json create mode 100644 src/plugins/vis_type_tagcloud/config.ts create mode 100644 src/plugins/vis_type_tagcloud/kibana.json rename src/{legacy/core_plugins => plugins}/vis_type_tagcloud/public/__snapshots__/tag_cloud_fn.test.ts.snap (100%) rename src/{legacy/core_plugins => plugins}/vis_type_tagcloud/public/_tag_cloud.scss (100%) rename src/{legacy/core_plugins => plugins}/vis_type_tagcloud/public/components/feedback_message.js (100%) rename src/{legacy/core_plugins => plugins}/vis_type_tagcloud/public/components/label.js (100%) rename src/{legacy/core_plugins => plugins}/vis_type_tagcloud/public/components/tag_cloud.js (100%) rename src/{legacy/core_plugins => plugins}/vis_type_tagcloud/public/components/tag_cloud_options.tsx (91%) rename src/{legacy/core_plugins => plugins}/vis_type_tagcloud/public/components/tag_cloud_visualization.js (100%) rename src/{legacy/core_plugins => plugins}/vis_type_tagcloud/public/index.scss (76%) rename src/{legacy/core_plugins => plugins}/vis_type_tagcloud/public/index.ts (93%) rename src/{legacy/core_plugins => plugins}/vis_type_tagcloud/public/plugin.ts (81%) rename src/{legacy/core_plugins => plugins}/vis_type_tagcloud/public/services.ts (82%) rename src/{legacy/core_plugins => plugins}/vis_type_tagcloud/public/tag_cloud_fn.test.ts (91%) rename src/{legacy/core_plugins => plugins}/vis_type_tagcloud/public/tag_cloud_fn.ts (96%) rename src/{legacy/core_plugins => plugins}/vis_type_tagcloud/public/tag_cloud_type.ts (98%) rename src/{legacy/core_plugins => plugins}/vis_type_tagcloud/public/types.ts (100%) rename src/{legacy/core_plugins/vis_type_tagcloud/public/legacy.ts => plugins/vis_type_tagcloud/server/index.ts} (57%) diff --git a/.i18nrc.json b/.i18nrc.json index d4286a7bd50e0..dc1be7f5a140b 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -49,7 +49,7 @@ "visTypeMarkdown": "src/plugins/vis_type_markdown", "visTypeMetric": "src/plugins/vis_type_metric", "visTypeTable": "src/plugins/vis_type_table", - "visTypeTagCloud": "src/legacy/core_plugins/vis_type_tagcloud", + "visTypeTagCloud": "src/plugins/vis_type_tagcloud", "visTypeTimeseries": ["src/legacy/core_plugins/vis_type_timeseries", "src/plugins/vis_type_timeseries"], "visTypeVega": "src/legacy/core_plugins/vis_type_vega", "visTypeVislib": "src/legacy/core_plugins/vis_type_vislib", diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/afterparamchange.png b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_tagcloud/afterparamchange.png similarity index 100% rename from src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/afterparamchange.png rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_tagcloud/afterparamchange.png diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/afterresize.png b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_tagcloud/afterresize.png similarity index 100% rename from src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/afterresize.png rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_tagcloud/afterresize.png diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/basicdraw.png b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_tagcloud/basicdraw.png similarity index 100% rename from src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/basicdraw.png rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_tagcloud/basicdraw.png diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/simpleload.png b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_tagcloud/simpleload.png similarity index 100% rename from src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/simpleload.png rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_tagcloud/simpleload.png diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud.js b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_tagcloud/tag_cloud.js similarity index 98% rename from src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud.js rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_tagcloud/tag_cloud.js index 152efe5667f18..8f08f6a1f37e6 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud.js +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_tagcloud/tag_cloud.js @@ -21,7 +21,6 @@ import expect from '@kbn/expect'; import _ from 'lodash'; import d3 from 'd3'; -import { TagCloud } from '../tag_cloud'; import { fromNode, delay } from 'bluebird'; import { ImageComparator } from 'test_utils/image_comparator'; import simpleloadPng from './simpleload.png'; @@ -29,6 +28,9 @@ import simpleloadPng from './simpleload.png'; // Replace with mock when converting to jest tests // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { seedColors } from '../../../../../../plugins/charts/public/services/colors/seed_colors'; +// Will be replaced with new path when tests are moved +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { TagCloud } from '../../../../../../plugins/vis_type_tagcloud/public/components/tag_cloud'; describe('tag cloud tests', function() { const minValue = 1; diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud_visualization.js b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_tagcloud/tag_cloud_visualization.js similarity index 89% rename from src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud_visualization.js rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_tagcloud/tag_cloud_visualization.js index 9e611861417cd..040ee18916fa2 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud_visualization.js +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_tagcloud/tag_cloud_visualization.js @@ -20,7 +20,6 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import { ImageComparator } from 'test_utils/image_comparator'; -import { createTagCloudVisualization } from '../tag_cloud_visualization'; import basicdrawPng from './basicdraw.png'; import afterresizePng from './afterresize.png'; import afterparamChange from './afterparamchange.png'; @@ -32,7 +31,14 @@ import { ExprVis } from '../../../../../../plugins/visualizations/public/express import { seedColors } from '../../../../../../plugins/charts/public/services/colors/seed_colors'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { BaseVisType } from '../../../../../../plugins/visualizations/public/vis_types/base_vis_type'; -import { createTagCloudVisTypeDefinition } from '../../tag_cloud_type'; +// Will be replaced with new path when tests are moved +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { createTagCloudVisTypeDefinition } from '../../../../../../plugins/vis_type_tagcloud/public/tag_cloud_type'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { createTagCloudVisualization } from '../../../../../../plugins/vis_type_tagcloud/public/components/tag_cloud_visualization'; +import { npStart } from 'ui/new_platform'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { setFormatService } from '../../../../../../plugins/vis_type_tagcloud/public/services'; const THRESHOLD = 0.65; const PIXEL_DIFF = 64; @@ -66,6 +72,8 @@ describe('TagCloudVisualizationTest', function() { }, }); + before(() => setFormatService(npStart.plugins.data.fieldFormats)); + beforeEach(ngMock.module('kibana')); describe('TagCloudVisualization - basics', function() { diff --git a/src/legacy/core_plugins/vis_type_tagcloud/index.ts b/src/legacy/core_plugins/vis_type_tagcloud/index.ts deleted file mode 100644 index 6f768131f2190..0000000000000 --- a/src/legacy/core_plugins/vis_type_tagcloud/index.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 { resolve } from 'path'; -import { Legacy } from 'kibana'; - -import { LegacyPluginApi, LegacyPluginInitializer } from '../../../../src/legacy/types'; - -const tagCloudPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPluginApi) => - new Plugin({ - id: 'tagcloud', - require: ['kibana', 'elasticsearch'], - publicDir: resolve(__dirname, 'public'), - uiExports: { - styleSheetPaths: resolve(__dirname, 'public/index.scss'), - hacks: [resolve(__dirname, 'public/legacy')], - injectDefaultVars: server => ({}), - }, - init: (server: Legacy.Server) => ({}), - config(Joi: any) { - return Joi.object({ - enabled: Joi.boolean().default(true), - }).default(); - }, - } as Legacy.PluginSpecOptions); - -// eslint-disable-next-line import/no-default-export -export default tagCloudPluginInitializer; diff --git a/src/legacy/core_plugins/vis_type_tagcloud/package.json b/src/legacy/core_plugins/vis_type_tagcloud/package.json deleted file mode 100644 index 4200ef264fece..0000000000000 --- a/src/legacy/core_plugins/vis_type_tagcloud/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "tagcloud", - "version": "kibana" -} diff --git a/src/plugins/vis_type_tagcloud/config.ts b/src/plugins/vis_type_tagcloud/config.ts new file mode 100644 index 0000000000000..6749bd83de39f --- /dev/null +++ b/src/plugins/vis_type_tagcloud/config.ts @@ -0,0 +1,26 @@ +/* + * 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 { schema, TypeOf } from '@kbn/config-schema'; + +export const configSchema = schema.object({ + enabled: schema.boolean({ defaultValue: true }), +}); + +export type ConfigSchema = TypeOf; diff --git a/src/plugins/vis_type_tagcloud/kibana.json b/src/plugins/vis_type_tagcloud/kibana.json new file mode 100644 index 0000000000000..dbc9a1b9ef692 --- /dev/null +++ b/src/plugins/vis_type_tagcloud/kibana.json @@ -0,0 +1,7 @@ +{ + "id": "visTypeTagcloud", + "version": "kibana", + "ui": true, + "server": true, + "requiredPlugins": ["data", "expressions", "visualizations", "charts"] +} diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/__snapshots__/tag_cloud_fn.test.ts.snap b/src/plugins/vis_type_tagcloud/public/__snapshots__/tag_cloud_fn.test.ts.snap similarity index 100% rename from src/legacy/core_plugins/vis_type_tagcloud/public/__snapshots__/tag_cloud_fn.test.ts.snap rename to src/plugins/vis_type_tagcloud/public/__snapshots__/tag_cloud_fn.test.ts.snap diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/_tag_cloud.scss b/src/plugins/vis_type_tagcloud/public/_tag_cloud.scss similarity index 100% rename from src/legacy/core_plugins/vis_type_tagcloud/public/_tag_cloud.scss rename to src/plugins/vis_type_tagcloud/public/_tag_cloud.scss diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/feedback_message.js b/src/plugins/vis_type_tagcloud/public/components/feedback_message.js similarity index 100% rename from src/legacy/core_plugins/vis_type_tagcloud/public/components/feedback_message.js rename to src/plugins/vis_type_tagcloud/public/components/feedback_message.js diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/label.js b/src/plugins/vis_type_tagcloud/public/components/label.js similarity index 100% rename from src/legacy/core_plugins/vis_type_tagcloud/public/components/label.js rename to src/plugins/vis_type_tagcloud/public/components/label.js diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud.js b/src/plugins/vis_type_tagcloud/public/components/tag_cloud.js similarity index 100% rename from src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud.js rename to src/plugins/vis_type_tagcloud/public/components/tag_cloud.js diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx b/src/plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx similarity index 91% rename from src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx rename to src/plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx index 7a64549edd747..d33576e4e5529 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx +++ b/src/plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx @@ -20,9 +20,9 @@ import React from 'react'; import { EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; -import { ValidatedDualRange } from '../../../../../../src/plugins/kibana_react/public'; -import { SelectOption, SwitchOption } from '../../../../../plugins/charts/public'; +import { VisOptionsProps } from '../../../vis_default_editor/public'; +import { ValidatedDualRange } from '../../../kibana_react/public'; +import { SelectOption, SwitchOption } from '../../../charts/public'; import { TagCloudVisParams } from '../types'; function TagCloudOptions({ stateParams, setValue, vis }: VisOptionsProps) { diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_visualization.js b/src/plugins/vis_type_tagcloud/public/components/tag_cloud_visualization.js similarity index 100% rename from src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_visualization.js rename to src/plugins/vis_type_tagcloud/public/components/tag_cloud_visualization.js diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/index.scss b/src/plugins/vis_type_tagcloud/public/index.scss similarity index 76% rename from src/legacy/core_plugins/vis_type_tagcloud/public/index.scss rename to src/plugins/vis_type_tagcloud/public/index.scss index a4fcf8418ce1c..e6893b9a2474c 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/index.scss +++ b/src/plugins/vis_type_tagcloud/public/index.scss @@ -1,5 +1,3 @@ -@import 'src/legacy/ui/public/styles/styling_constants'; - // Prefix all styles with "tgc" to avoid conflicts. // Examples // tgcChart diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/index.ts b/src/plugins/vis_type_tagcloud/public/index.ts similarity index 93% rename from src/legacy/core_plugins/vis_type_tagcloud/public/index.ts rename to src/plugins/vis_type_tagcloud/public/index.ts index 90e6305262caa..ff27d96b710fa 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/index.ts +++ b/src/plugins/vis_type_tagcloud/public/index.ts @@ -17,7 +17,7 @@ * under the License. */ -import { PluginInitializerContext } from '../../../../core/public'; +import { PluginInitializerContext } from 'kibana/public'; import { TagCloudPlugin as Plugin } from './plugin'; export function plugin(initializerContext: PluginInitializerContext) { diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/plugin.ts b/src/plugins/vis_type_tagcloud/public/plugin.ts similarity index 81% rename from src/legacy/core_plugins/vis_type_tagcloud/public/plugin.ts rename to src/plugins/vis_type_tagcloud/public/plugin.ts index 1061271aa315b..6978186058b1d 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/plugin.ts +++ b/src/plugins/vis_type_tagcloud/public/plugin.ts @@ -17,15 +17,18 @@ * under the License. */ -import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../../core/public'; -import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public'; -import { VisualizationsSetup } from '../../../../plugins/visualizations/public'; -import { ChartsPluginSetup } from '../../../../plugins/charts/public'; +import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'kibana/public'; +import { Plugin as ExpressionsPublicPlugin } from '../../expressions/public'; +import { VisualizationsSetup } from '../../visualizations/public'; +import { ChartsPluginSetup } from '../../charts/public'; import { createTagCloudFn } from './tag_cloud_fn'; import { createTagCloudVisTypeDefinition } from './tag_cloud_type'; -import { DataPublicPluginStart } from '../../../../plugins/data/public'; +import { DataPublicPluginStart } from '../../data/public'; import { setFormatService } from './services'; +import { ConfigSchema } from '../config'; + +import './index.scss'; /** @internal */ export interface TagCloudPluginSetupDependencies { @@ -46,9 +49,9 @@ export interface TagCloudVisPluginStartDependencies { /** @internal */ export class TagCloudPlugin implements Plugin { - initializerContext: PluginInitializerContext; + initializerContext: PluginInitializerContext; - constructor(initializerContext: PluginInitializerContext) { + constructor(initializerContext: PluginInitializerContext) { this.initializerContext = initializerContext; } diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/services.ts b/src/plugins/vis_type_tagcloud/public/services.ts similarity index 82% rename from src/legacy/core_plugins/vis_type_tagcloud/public/services.ts rename to src/plugins/vis_type_tagcloud/public/services.ts index 272bed3e91a08..f6002afc66493 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/services.ts +++ b/src/plugins/vis_type_tagcloud/public/services.ts @@ -17,11 +17,9 @@ * under the License. */ -import { createGetterSetter } from '../../../../plugins/kibana_utils/public'; -import { DataPublicPluginStart } from '../../../../plugins/data/public'; +import { createGetterSetter } from '../../kibana_utils/public'; +import { DataPublicPluginStart } from '../../data/public'; export const [getFormatService, setFormatService] = createGetterSetter< DataPublicPluginStart['fieldFormats'] >('data.fieldFormats'); - -export { npStart } from 'ui/new_platform'; diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_fn.test.ts b/src/plugins/vis_type_tagcloud/public/tag_cloud_fn.test.ts similarity index 91% rename from src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_fn.test.ts rename to src/plugins/vis_type_tagcloud/public/tag_cloud_fn.test.ts index 65c54766133d1..eb16b0855a138 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_fn.test.ts +++ b/src/plugins/vis_type_tagcloud/public/tag_cloud_fn.test.ts @@ -19,8 +19,7 @@ import { createTagCloudFn } from './tag_cloud_fn'; -// eslint-disable-next-line -import { functionWrapper } from '../../../../plugins/expressions/common/expression_functions/specs/tests/utils'; +import { functionWrapper } from '../../expressions/common/expression_functions/specs/tests/utils'; describe('interpreter/functions#tagcloud', () => { const fn = functionWrapper(createTagCloudFn()); diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_fn.ts b/src/plugins/vis_type_tagcloud/public/tag_cloud_fn.ts similarity index 96% rename from src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_fn.ts rename to src/plugins/vis_type_tagcloud/public/tag_cloud_fn.ts index 31c7fd118cefd..05cf05ab00b75 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_fn.ts +++ b/src/plugins/vis_type_tagcloud/public/tag_cloud_fn.ts @@ -19,11 +19,7 @@ import { i18n } from '@kbn/i18n'; -import { - ExpressionFunctionDefinition, - KibanaDatatable, - Render, -} from '../../../../plugins/expressions/public'; +import { ExpressionFunctionDefinition, KibanaDatatable, Render } from '../../expressions/public'; import { TagCloudVisParams } from './types'; const name = 'tagcloud'; diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_type.ts b/src/plugins/vis_type_tagcloud/public/tag_cloud_type.ts similarity index 98% rename from src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_type.ts rename to src/plugins/vis_type_tagcloud/public/tag_cloud_type.ts index b7dfa62c93fb9..5a8cc3004a315 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_type.ts +++ b/src/plugins/vis_type_tagcloud/public/tag_cloud_type.ts @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; -import { Schemas } from '../../../../plugins/vis_default_editor/public'; +import { Schemas } from '../../vis_default_editor/public'; import { TagCloudOptions } from './components/tag_cloud_options'; diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/types.ts b/src/plugins/vis_type_tagcloud/public/types.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_tagcloud/public/types.ts rename to src/plugins/vis_type_tagcloud/public/types.ts diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/legacy.ts b/src/plugins/vis_type_tagcloud/server/index.ts similarity index 57% rename from src/legacy/core_plugins/vis_type_tagcloud/public/legacy.ts rename to src/plugins/vis_type_tagcloud/server/index.ts index f70789edc66ba..bd9656b29c524 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/legacy.ts +++ b/src/plugins/vis_type_tagcloud/server/index.ts @@ -17,18 +17,18 @@ * under the License. */ -import { PluginInitializerContext } from 'kibana/public'; -import { npSetup, npStart } from 'ui/new_platform'; -import { TagCloudPluginSetupDependencies } from './plugin'; -import { plugin } from '.'; +import { PluginConfigDescriptor } from 'kibana/server'; -const plugins: Readonly = { - expressions: npSetup.plugins.expressions, - visualizations: npSetup.plugins.visualizations, - charts: npSetup.plugins.charts, -}; +import { configSchema, ConfigSchema } from '../config'; -const pluginInstance = plugin({} as PluginInitializerContext); +export const config: PluginConfigDescriptor = { + schema: configSchema, + deprecations: ({ renameFromRoot }) => [ + renameFromRoot('tagcloud.enabled', 'vis_type_tagcloud.enabled'), + ], +}; -export const setup = pluginInstance.setup(npSetup.core, plugins); -export const start = pluginInstance.start(npStart.core, { data: npStart.plugins.data }); +export const plugin = () => ({ + setup() {}, + start() {}, +}); From 8243fc4b41e358a76638ea8a953f48dfa4e1e92c Mon Sep 17 00:00:00 2001 From: Dario Gieselaar Date: Thu, 23 Apr 2020 11:34:28 +0200 Subject: [PATCH 21/39] Add APM paths to path-labeller.yml (#63772) * Add APM paths to path-labeller.yml * Sort alphabetically on labels Co-authored-by: Elastic Machine --- .github/paths-labeller.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/paths-labeller.yml b/.github/paths-labeller.yml index 544dd577313df..89e0af270c54d 100644 --- a/.github/paths-labeller.yml +++ b/.github/paths-labeller.yml @@ -8,6 +8,9 @@ - "Feature:ExpressionLanguage": - "src/plugins/expressions/**/*.*" - "src/plugins/bfetch/**/*.*" + - "Team:apm" + - "x-pack/plugins/apm/**/*.*" + - "x-pack/legacy/plugins/apm/**/*.*" - "Team:uptime": - "x-pack/plugins/uptime/**/*.*" - "x-pack/legacy/plugins/uptime/**/*.*" From d37bee99aac5095d7cfb8a111546aae073d06c21 Mon Sep 17 00:00:00 2001 From: John Schulz Date: Thu, 23 Apr 2020 05:47:42 -0400 Subject: [PATCH 22/39] [Ingest] Move some server lifecycle types out of common. (#64252) The AgentService, ESIndexPatternService, and IngestManager{Setup,Start}{Deps,Contract} values were in common, but they are server only. --- .../server/endpoint_app_context_services.ts | 2 +- .../plugins/endpoint/server/index_pattern.ts | 2 +- x-pack/plugins/endpoint/server/mocks.ts | 6 ++-- x-pack/plugins/endpoint/server/plugin.test.ts | 4 +-- x-pack/plugins/endpoint/server/plugin.ts | 4 +-- .../server/routes/metadata/metadata.test.ts | 2 +- .../ingest_manager/common/types/index.ts | 34 ------------------- x-pack/plugins/ingest_manager/server/index.ts | 6 ++++ .../plugins/ingest_manager/server/plugin.ts | 32 +++++++++++++---- .../server/services/es_index_pattern.ts | 2 +- .../ingest_manager/server/services/index.ts | 26 ++++++++++++++ 11 files changed, 68 insertions(+), 52 deletions(-) diff --git a/x-pack/plugins/endpoint/server/endpoint_app_context_services.ts b/x-pack/plugins/endpoint/server/endpoint_app_context_services.ts index 4d77c5c68c69f..b087405c7bc5b 100644 --- a/x-pack/plugins/endpoint/server/endpoint_app_context_services.ts +++ b/x-pack/plugins/endpoint/server/endpoint_app_context_services.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { IndexPatternRetriever } from './index_pattern'; -import { AgentService } from '../../ingest_manager/common/types'; +import { AgentService } from '../../ingest_manager/server'; /** * A singleton that holds shared services that are initialized during the start up phase diff --git a/x-pack/plugins/endpoint/server/index_pattern.ts b/x-pack/plugins/endpoint/server/index_pattern.ts index 05cfd91f2777a..dcedd27fc5a3d 100644 --- a/x-pack/plugins/endpoint/server/index_pattern.ts +++ b/x-pack/plugins/endpoint/server/index_pattern.ts @@ -5,7 +5,7 @@ */ import { Logger, LoggerFactory, RequestHandlerContext } from 'kibana/server'; import { EndpointAppConstants } from '../common/types'; -import { ESIndexPatternService } from '../../ingest_manager/common/types'; +import { ESIndexPatternService } from '../../ingest_manager/server'; export interface IndexPatternRetriever { getIndexPattern(ctx: RequestHandlerContext, datasetPath: string): Promise; diff --git a/x-pack/plugins/endpoint/server/mocks.ts b/x-pack/plugins/endpoint/server/mocks.ts index 8d677cf37f066..519ca15cf8427 100644 --- a/x-pack/plugins/endpoint/server/mocks.ts +++ b/x-pack/plugins/endpoint/server/mocks.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AgentService, IngestManagerStartupContract } from '../../ingest_manager/common/types'; +import { AgentService, IngestManagerStartContract } from '../../ingest_manager/server'; /** * Creates a mock IndexPatternRetriever for use in tests. @@ -46,9 +46,9 @@ export const createMockAgentService = (): jest.Mocked => { * @param indexPattern a string index pattern to return when called by a test * @returns the same value as `indexPattern` parameter */ -export const createMockIngestManagerStartupContract = ( +export const createMockIngestManagerStartContract = ( indexPattern: string -): IngestManagerStartupContract => { +): IngestManagerStartContract => { return { esIndexPatternService: { getESIndexPattern: jest.fn().mockResolvedValue(indexPattern), diff --git a/x-pack/plugins/endpoint/server/plugin.test.ts b/x-pack/plugins/endpoint/server/plugin.test.ts index 44b9c3eabbd6d..45e9591a14975 100644 --- a/x-pack/plugins/endpoint/server/plugin.test.ts +++ b/x-pack/plugins/endpoint/server/plugin.test.ts @@ -11,7 +11,7 @@ import { } from './plugin'; import { coreMock } from '../../../../src/core/server/mocks'; import { PluginSetupContract } from '../../features/server'; -import { createMockIngestManagerStartupContract } from './mocks'; +import { createMockIngestManagerStartContract } from './mocks'; describe('test endpoint plugin', () => { let plugin: EndpointPlugin; @@ -51,7 +51,7 @@ describe('test endpoint plugin', () => { it('test properly start plugin', async () => { mockedEndpointPluginStartDependencies = { - ingestManager: createMockIngestManagerStartupContract(''), + ingestManager: createMockIngestManagerStartContract(''), }; await plugin.start(mockCoreStart, mockedEndpointPluginStartDependencies); expect(plugin.getEndpointAppContextService().getAgentService()).toBeTruthy(); diff --git a/x-pack/plugins/endpoint/server/plugin.ts b/x-pack/plugins/endpoint/server/plugin.ts index f7ff32bdbfcbf..f3cc569ad16a7 100644 --- a/x-pack/plugins/endpoint/server/plugin.ts +++ b/x-pack/plugins/endpoint/server/plugin.ts @@ -14,13 +14,13 @@ import { registerResolverRoutes } from './routes/resolver'; import { registerIndexPatternRoute } from './routes/index_pattern'; import { registerEndpointRoutes } from './routes/metadata'; import { IngestIndexPatternRetriever } from './index_pattern'; -import { IngestManagerStartupContract } from '../../ingest_manager/common/types'; +import { IngestManagerStartContract } from '../../ingest_manager/server'; import { EndpointAppContextService } from './endpoint_app_context_services'; export type EndpointPluginStart = void; export type EndpointPluginSetup = void; export interface EndpointPluginStartDependencies { - ingestManager: IngestManagerStartupContract; + ingestManager: IngestManagerStartContract; } export interface EndpointPluginSetupDependencies { diff --git a/x-pack/plugins/endpoint/server/routes/metadata/metadata.test.ts b/x-pack/plugins/endpoint/server/routes/metadata/metadata.test.ts index 84930118df238..8f0c0b4c2efaf 100644 --- a/x-pack/plugins/endpoint/server/routes/metadata/metadata.test.ts +++ b/x-pack/plugins/endpoint/server/routes/metadata/metadata.test.ts @@ -26,7 +26,7 @@ import { registerEndpointRoutes } from './index'; import { EndpointConfigSchema } from '../../config'; import * as data from '../../test_data/all_metadata_data.json'; import { createMockAgentService, createMockMetadataIndexPatternRetriever } from '../../mocks'; -import { AgentService } from '../../../../ingest_manager/common/types'; +import { AgentService } from '../../../../ingest_manager/server'; import Boom from 'boom'; import { EndpointAppContextService } from '../../endpoint_app_context_services'; diff --git a/x-pack/plugins/ingest_manager/common/types/index.ts b/x-pack/plugins/ingest_manager/common/types/index.ts index 438db9a25b8ee..42f7a9333118e 100644 --- a/x-pack/plugins/ingest_manager/common/types/index.ts +++ b/x-pack/plugins/ingest_manager/common/types/index.ts @@ -3,43 +3,9 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'kibana/server'; -import { AgentStatus } from './models'; - export * from './models'; export * from './rest_spec'; -/** - * Service to return the index pattern of EPM packages - */ -export interface ESIndexPatternService { - getESIndexPattern( - savedObjectsClient: SavedObjectsClientContract, - pkgName: string, - datasetPath: string - ): Promise; -} - -/** - * Describes public IngestManager plugin contract returned at the `startup` stage. - */ -export interface IngestManagerStartupContract { - esIndexPatternService: ESIndexPatternService; - agentService: AgentService; -} - -/** - * A service that provides exported functions that return information about an Agent - */ -export interface AgentService { - /** - * Return the status by the Agent's id - * @param soClient - * @param agentId - */ - getAgentStatusById(soClient: SavedObjectsClientContract, agentId: string): Promise; -} - export interface IngestManagerConfigType { enabled: boolean; epm: { diff --git a/x-pack/plugins/ingest_manager/server/index.ts b/x-pack/plugins/ingest_manager/server/index.ts index 0f49de0d5fdd1..851a58f5adac2 100644 --- a/x-pack/plugins/ingest_manager/server/index.ts +++ b/x-pack/plugins/ingest_manager/server/index.ts @@ -6,6 +6,12 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { PluginInitializerContext } from 'src/core/server'; import { IngestManagerPlugin } from './plugin'; +export { AgentService, ESIndexPatternService } from './services'; +export { + IngestManagerSetupContract, + IngestManagerSetupDeps, + IngestManagerStartContract, +} from './plugin'; export const config = { exposeToBrowser: { diff --git a/x-pack/plugins/ingest_manager/server/plugin.ts b/x-pack/plugins/ingest_manager/server/plugin.ts index 80e35eadb4603..55b1d2961bca1 100644 --- a/x-pack/plugins/ingest_manager/server/plugin.ts +++ b/x-pack/plugins/ingest_manager/server/plugin.ts @@ -11,9 +11,7 @@ import { Plugin, PluginInitializerContext, SavedObjectsServiceStart, - RecursiveReadonly, } from 'kibana/server'; -import { deepFreeze } from '../../../../src/core/utils'; import { LicensingPluginSetup } from '../../licensing/server'; import { EncryptedSavedObjectsPluginStart } from '../../encrypted_saved_objects/server'; import { SecurityPluginSetup } from '../../security/server'; @@ -39,8 +37,9 @@ import { registerInstallScriptRoutes, } from './routes'; -import { IngestManagerConfigType, IngestManagerStartupContract } from '../common'; +import { IngestManagerConfigType } from '../common'; import { appContextService, ESIndexPatternSavedObjectService } from './services'; +import { ESIndexPatternService, AgentService } from './services'; import { getAgentStatusById } from './services/agents'; export interface IngestManagerSetupDeps { @@ -49,6 +48,8 @@ export interface IngestManagerSetupDeps { features?: FeaturesPluginSetup; } +export type IngestManagerStartDeps = object; + export interface IngestManagerAppContext { encryptedSavedObjects: EncryptedSavedObjectsPluginStart; security?: SecurityPluginSetup; @@ -56,6 +57,8 @@ export interface IngestManagerAppContext { savedObjects: SavedObjectsServiceStart; } +export type IngestManagerSetupContract = void; + const allSavedObjectTypes = [ OUTPUT_SAVED_OBJECT_TYPE, AGENT_CONFIG_SAVED_OBJECT_TYPE, @@ -66,7 +69,22 @@ const allSavedObjectTypes = [ ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, ]; -export class IngestManagerPlugin implements Plugin { +/** + * Describes public IngestManager plugin contract returned at the `startup` stage. + */ +export interface IngestManagerStartContract { + esIndexPatternService: ESIndexPatternService; + agentService: AgentService; +} + +export class IngestManagerPlugin + implements + Plugin< + IngestManagerSetupContract, + IngestManagerStartContract, + IngestManagerSetupDeps, + IngestManagerStartDeps + > { private config$: Observable; private security: SecurityPluginSetup | undefined; @@ -140,19 +158,19 @@ export class IngestManagerPlugin implements Plugin> { + ) { appContextService.start({ encryptedSavedObjects: plugins.encryptedSavedObjects, security: this.security, config$: this.config$, savedObjects: core.savedObjects, }); - return deepFreeze({ + return { esIndexPatternService: new ESIndexPatternSavedObjectService(), agentService: { getAgentStatusById, }, - }); + }; } public async stop() { diff --git a/x-pack/plugins/ingest_manager/server/services/es_index_pattern.ts b/x-pack/plugins/ingest_manager/server/services/es_index_pattern.ts index e2c27cdacda2f..fc2fe6d1c40e8 100644 --- a/x-pack/plugins/ingest_manager/server/services/es_index_pattern.ts +++ b/x-pack/plugins/ingest_manager/server/services/es_index_pattern.ts @@ -5,7 +5,7 @@ */ import { SavedObjectsClientContract } from 'kibana/server'; import { getInstallation } from './epm/packages'; -import { ESIndexPatternService } from '../../common/types'; +import { ESIndexPatternService } from '../../server'; export class ESIndexPatternSavedObjectService implements ESIndexPatternService { public async getESIndexPattern( diff --git a/x-pack/plugins/ingest_manager/server/services/index.ts b/x-pack/plugins/ingest_manager/server/services/index.ts index 4dfc3cb58b733..1b0f174cc1a8e 100644 --- a/x-pack/plugins/ingest_manager/server/services/index.ts +++ b/x-pack/plugins/ingest_manager/server/services/index.ts @@ -3,9 +3,35 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { SavedObjectsClientContract } from 'kibana/server'; +import { AgentStatus } from '../../common/types/models'; + export { appContextService } from './app_context'; export { ESIndexPatternSavedObjectService } from './es_index_pattern'; +/** + * Service to return the index pattern of EPM packages + */ +export interface ESIndexPatternService { + getESIndexPattern( + savedObjectsClient: SavedObjectsClientContract, + pkgName: string, + datasetPath: string + ): Promise; +} + +/** + * A service that provides exported functions that return information about an Agent + */ +export interface AgentService { + /** + * Return the status by the Agent's id + * @param soClient + * @param agentId + */ + getAgentStatusById(soClient: SavedObjectsClientContract, agentId: string): Promise; +} + // Saved object services export { datasourceService } from './datasource'; export { agentConfigService } from './agent_config'; From dba2ffdd84e4c5bffe76a950ae65b27329aa64d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= Date: Thu, 23 Apr 2020 11:26:43 +0100 Subject: [PATCH 23/39] [Telemetry] Fix optIn telemetry report bug (#64214) --- src/plugins/telemetry/public/mocks.ts | 23 +++- .../public/services/telemetry_service.test.ts | 103 +++++++++++++++--- .../public/services/telemetry_service.ts | 16 ++- .../server/routes/telemetry_opt_in.ts | 28 +++-- 4 files changed, 137 insertions(+), 33 deletions(-) diff --git a/src/plugins/telemetry/public/mocks.ts b/src/plugins/telemetry/public/mocks.ts index 4e0f02242961a..fd88f520205f5 100644 --- a/src/plugins/telemetry/public/mocks.ts +++ b/src/plugins/telemetry/public/mocks.ts @@ -25,11 +25,20 @@ import { httpServiceMock } from '../../../core/public/http/http_service.mock'; import { notificationServiceMock } from '../../../core/public/notifications/notifications_service.mock'; import { TelemetryService } from './services/telemetry_service'; import { TelemetryNotifications } from './services/telemetry_notifications/telemetry_notifications'; -import { TelemetryPluginStart } from './plugin'; +import { TelemetryPluginStart, TelemetryPluginConfig } from './plugin'; + +// The following is to be able to access private methods +/* eslint-disable dot-notation */ + +export interface TelemetryServiceMockOptions { + reportOptInStatusChange?: boolean; + config?: Partial; +} export function mockTelemetryService({ reportOptInStatusChange, -}: { reportOptInStatusChange?: boolean } = {}) { + config: configOverride = {}, +}: TelemetryServiceMockOptions = {}) { const config = { enabled: true, url: 'http://localhost', @@ -39,14 +48,22 @@ export function mockTelemetryService({ banner: true, allowChangingOptInStatus: true, telemetryNotifyUserAboutOptInDefault: true, + ...configOverride, }; - return new TelemetryService({ + const telemetryService = new TelemetryService({ config, http: httpServiceMock.createStartContract(), notifications: notificationServiceMock.createStartContract(), reportOptInStatusChange, }); + + const originalReportOptInStatus = telemetryService['reportOptInStatus']; + telemetryService['reportOptInStatus'] = jest.fn().mockImplementation(optInPayload => { + return originalReportOptInStatus(optInPayload); // Actually calling the original method + }); + + return telemetryService; } export function mockTelemetryNotifications({ diff --git a/src/plugins/telemetry/public/services/telemetry_service.test.ts b/src/plugins/telemetry/public/services/telemetry_service.test.ts index 0a49b0ae3084e..16faa0cfc7536 100644 --- a/src/plugins/telemetry/public/services/telemetry_service.test.ts +++ b/src/plugins/telemetry/public/services/telemetry_service.test.ts @@ -67,17 +67,31 @@ describe('TelemetryService', () => { }); describe('setOptIn', () => { + it('does not call the api if canChangeOptInStatus==false', async () => { + const telemetryService = mockTelemetryService({ + reportOptInStatusChange: false, + config: { allowChangingOptInStatus: false }, + }); + expect(await telemetryService.setOptIn(true)).toBe(false); + + expect(telemetryService['http'].post).toBeCalledTimes(0); + }); + it('calls api if canChangeOptInStatus', async () => { - const telemetryService = mockTelemetryService({ reportOptInStatusChange: false }); - telemetryService.getCanChangeOptInStatus = jest.fn().mockReturnValue(true); + const telemetryService = mockTelemetryService({ + reportOptInStatusChange: false, + config: { allowChangingOptInStatus: true }, + }); await telemetryService.setOptIn(true); expect(telemetryService['http'].post).toBeCalledTimes(1); }); it('sends enabled true if optedIn: true', async () => { - const telemetryService = mockTelemetryService({ reportOptInStatusChange: false }); - telemetryService.getCanChangeOptInStatus = jest.fn().mockReturnValue(true); + const telemetryService = mockTelemetryService({ + reportOptInStatusChange: false, + config: { allowChangingOptInStatus: true }, + }); const optedIn = true; await telemetryService.setOptIn(optedIn); @@ -87,8 +101,10 @@ describe('TelemetryService', () => { }); it('sends enabled false if optedIn: false', async () => { - const telemetryService = mockTelemetryService({ reportOptInStatusChange: false }); - telemetryService.getCanChangeOptInStatus = jest.fn().mockReturnValue(true); + const telemetryService = mockTelemetryService({ + reportOptInStatusChange: false, + config: { allowChangingOptInStatus: true }, + }); const optedIn = false; await telemetryService.setOptIn(optedIn); @@ -98,9 +114,10 @@ describe('TelemetryService', () => { }); it('does not call reportOptInStatus if reportOptInStatusChange is false', async () => { - const telemetryService = mockTelemetryService({ reportOptInStatusChange: false }); - telemetryService.getCanChangeOptInStatus = jest.fn().mockReturnValue(true); - telemetryService['reportOptInStatus'] = jest.fn(); + const telemetryService = mockTelemetryService({ + reportOptInStatusChange: false, + config: { allowChangingOptInStatus: true }, + }); await telemetryService.setOptIn(true); expect(telemetryService['reportOptInStatus']).toBeCalledTimes(0); @@ -108,9 +125,10 @@ describe('TelemetryService', () => { }); it('calls reportOptInStatus if reportOptInStatusChange is true', async () => { - const telemetryService = mockTelemetryService({ reportOptInStatusChange: true }); - telemetryService.getCanChangeOptInStatus = jest.fn().mockReturnValue(true); - telemetryService['reportOptInStatus'] = jest.fn(); + const telemetryService = mockTelemetryService({ + reportOptInStatusChange: true, + config: { allowChangingOptInStatus: true }, + }); await telemetryService.setOptIn(true); expect(telemetryService['reportOptInStatus']).toBeCalledTimes(1); @@ -118,9 +136,10 @@ describe('TelemetryService', () => { }); it('adds an error toast on api error', async () => { - const telemetryService = mockTelemetryService({ reportOptInStatusChange: false }); - telemetryService.getCanChangeOptInStatus = jest.fn().mockReturnValue(true); - telemetryService['reportOptInStatus'] = jest.fn(); + const telemetryService = mockTelemetryService({ + reportOptInStatusChange: false, + config: { allowChangingOptInStatus: true }, + }); telemetryService['http'].post = jest.fn().mockImplementation((url: string) => { if (url === '/api/telemetry/v2/optIn') { throw Error('failed to update opt in.'); @@ -133,9 +152,13 @@ describe('TelemetryService', () => { expect(telemetryService['notifications'].toasts.addError).toBeCalledTimes(1); }); + // This one should not happen because the entire method is fully caught but hey! :) it('adds an error toast on reportOptInStatus error', async () => { - const telemetryService = mockTelemetryService({ reportOptInStatusChange: true }); - telemetryService.getCanChangeOptInStatus = jest.fn().mockReturnValue(true); + const telemetryService = mockTelemetryService({ + reportOptInStatusChange: true, + config: { allowChangingOptInStatus: true }, + }); + telemetryService['reportOptInStatus'] = jest.fn().mockImplementation(() => { throw Error('failed to report OptIn Status.'); }); @@ -146,4 +169,50 @@ describe('TelemetryService', () => { expect(telemetryService['notifications'].toasts.addError).toBeCalledTimes(1); }); }); + + describe('getTelemetryUrl', () => { + it('should return the config.url parameter', async () => { + const url = 'http://test.com'; + const telemetryService = mockTelemetryService({ + config: { url }, + }); + + expect(telemetryService.getTelemetryUrl()).toBe(url); + }); + }); + + describe('setUserHasSeenNotice', () => { + it('should hit the API and change the config', async () => { + const telemetryService = mockTelemetryService({ + config: { telemetryNotifyUserAboutOptInDefault: undefined }, + }); + + expect(telemetryService.userHasSeenOptedInNotice).toBe(undefined); + expect(telemetryService.getUserHasSeenOptedInNotice()).toBe(false); + await telemetryService.setUserHasSeenNotice(); + expect(telemetryService['http'].put).toBeCalledTimes(1); + expect(telemetryService.userHasSeenOptedInNotice).toBe(true); + expect(telemetryService.getUserHasSeenOptedInNotice()).toBe(true); + }); + + it('should show a toast notification if the request fail', async () => { + const telemetryService = mockTelemetryService({ + config: { telemetryNotifyUserAboutOptInDefault: undefined }, + }); + + telemetryService['http'].put = jest.fn().mockImplementation((url: string) => { + if (url === '/api/telemetry/v2/userHasSeenNotice') { + throw Error('failed to update opt in.'); + } + }); + + expect(telemetryService.userHasSeenOptedInNotice).toBe(undefined); + expect(telemetryService.getUserHasSeenOptedInNotice()).toBe(false); + await telemetryService.setUserHasSeenNotice(); + expect(telemetryService['http'].put).toBeCalledTimes(1); + expect(telemetryService['notifications'].toasts.addError).toBeCalledTimes(1); + expect(telemetryService.userHasSeenOptedInNotice).toBe(false); + expect(telemetryService.getUserHasSeenOptedInNotice()).toBe(false); + }); + }); }); diff --git a/src/plugins/telemetry/public/services/telemetry_service.ts b/src/plugins/telemetry/public/services/telemetry_service.ts index cac4e3fdf5f50..6d87a74197fe5 100644 --- a/src/plugins/telemetry/public/services/telemetry_service.ts +++ b/src/plugins/telemetry/public/services/telemetry_service.ts @@ -122,11 +122,15 @@ export class TelemetryService { } try { - await this.http.post('/api/telemetry/v2/optIn', { + // Report the option to the Kibana server to store the settings. + // It returns the encrypted update to send to the telemetry cluster [{cluster_uuid, opt_in_status}] + const optInPayload = await this.http.post('/api/telemetry/v2/optIn', { body: JSON.stringify({ enabled: optedIn }), }); if (this.reportOptInStatusChange) { - await this.reportOptInStatus(optedIn); + // Use the response to report about the change to the remote telemetry cluster. + // If it's opt-out, this will be the last communication to the remote service. + await this.reportOptInStatus(optInPayload); } this.isOptedIn = optedIn; } catch (err) { @@ -162,7 +166,11 @@ export class TelemetryService { } }; - private reportOptInStatus = async (OptInStatus: boolean): Promise => { + /** + * Pushes the encrypted payload [{cluster_uuid, opt_in_status}] to the remote telemetry service + * @param optInPayload [{cluster_uuid, opt_in_status}] encrypted by the server into an array of strings + */ + private reportOptInStatus = async (optInPayload: string[]): Promise => { const telemetryOptInStatusUrl = this.getOptInStatusUrl(); try { @@ -171,7 +179,7 @@ export class TelemetryService { headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ enabled: OptInStatus }), + body: JSON.stringify(optInPayload), }); } catch (err) { // Sending the ping is best-effort. Telemetry tries to send the ping once and discards it immediately if sending fails. diff --git a/src/plugins/telemetry/server/routes/telemetry_opt_in.ts b/src/plugins/telemetry/server/routes/telemetry_opt_in.ts index e65ade0ab8aaa..4ed5dbf251275 100644 --- a/src/plugins/telemetry/server/routes/telemetry_opt_in.ts +++ b/src/plugins/telemetry/server/routes/telemetry_opt_in.ts @@ -22,7 +22,10 @@ import { Observable } from 'rxjs'; import { take } from 'rxjs/operators'; import { schema } from '@kbn/config-schema'; import { IRouter } from 'kibana/server'; -import { TelemetryCollectionManagerPluginSetup } from 'src/plugins/telemetry_collection_manager/server'; +import { + StatsGetterConfig, + TelemetryCollectionManagerPluginSetup, +} from 'src/plugins/telemetry_collection_manager/server'; import { getTelemetryAllowChangingOptInStatus } from '../../common/telemetry_config'; import { sendTelemetryOptInStatus } from './telemetry_opt_in_stats'; @@ -79,23 +82,30 @@ export function registerTelemetryOptInRoutes({ }); } + const statsGetterConfig: StatsGetterConfig = { + start: moment() + .subtract(20, 'minutes') + .toISOString(), + end: moment().toISOString(), + unencrypted: false, + }; + + const optInStatus = await telemetryCollectionManager.getOptInStats( + newOptInStatus, + statsGetterConfig + ); + if (config.sendUsageFrom === 'server') { const optInStatusUrl = config.optInStatusUrl; await sendTelemetryOptInStatus( telemetryCollectionManager, { optInStatusUrl, newOptInStatus }, - { - start: moment() - .subtract(20, 'minutes') - .toISOString(), - end: moment().toISOString(), - unencrypted: false, - } + statsGetterConfig ); } await updateTelemetrySavedObject(context.core.savedObjects.client, attributes); - return res.ok({}); + return res.ok({ body: optInStatus }); } ); } From 4d979930aa62e7884f9c1aa4a4f92671e8fdaf93 Mon Sep 17 00:00:00 2001 From: Peter Pisljar Date: Thu, 23 Apr 2020 13:08:00 +0200 Subject: [PATCH 24/39] moves all vis state updates into one place (#61960) --- src/plugins/visualizations/public/index.ts | 2 + .../public/legacy/vis_update.js | 57 -------------- .../public/legacy/vis_update_state.js | 75 +++++++++++++++++++ src/plugins/visualizations/public/vis.ts | 5 -- 4 files changed, 77 insertions(+), 62 deletions(-) delete mode 100644 src/plugins/visualizations/public/legacy/vis_update.js diff --git a/src/plugins/visualizations/public/index.ts b/src/plugins/visualizations/public/index.ts index 7df420e7ba585..e475684ed5934 100644 --- a/src/plugins/visualizations/public/index.ts +++ b/src/plugins/visualizations/public/index.ts @@ -43,6 +43,8 @@ export type VisualizeEmbeddableContract = PublicContract; export { VisualizeInput } from './embeddable'; export type ExprVis = ExprVisClass; export { SchemaConfig } from './legacy/build_pipeline'; +// @ts-ignore +export { updateOldState } from './legacy/vis_update_state'; export { PersistedState } from './persisted_state'; export { VisualizationController, diff --git a/src/plugins/visualizations/public/legacy/vis_update.js b/src/plugins/visualizations/public/legacy/vis_update.js deleted file mode 100644 index 338a322e6aa57..0000000000000 --- a/src/plugins/visualizations/public/legacy/vis_update.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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. - */ - -// TODO: this should be moved to vis_update_state -// Currently the migration takes place in Vis when calling setCurrentState. -// It should rather convert the raw saved object before starting to instantiate -// any JavaScript classes from it. -const updateVisualizationConfig = (stateConfig, config) => { - if (!stateConfig || stateConfig.seriesParams) return; - if (!['line', 'area', 'histogram'].includes(config.type)) return; - - // update value axis options - const isUserDefinedYAxis = config.setYExtents; - const defaultYExtents = config.defaultYExtents; - const mode = ['stacked', 'overlap'].includes(config.mode) ? 'normal' : config.mode || 'normal'; - config.valueAxes[0].scale = { - ...config.valueAxes[0].scale, - type: config.scale || 'linear', - setYExtents: config.setYExtents || false, - defaultYExtents: config.defaultYExtents || false, - boundsMargin: defaultYExtents ? config.boundsMargin : 0, - min: isUserDefinedYAxis ? config.yAxis.min : undefined, - max: isUserDefinedYAxis ? config.yAxis.max : undefined, - mode: mode, - }; - - // update series options - const interpolate = config.smoothLines ? 'cardinal' : config.interpolate; - const stacked = ['stacked', 'percentage', 'wiggle', 'silhouette'].includes(config.mode); - config.seriesParams[0] = { - ...config.seriesParams[0], - type: config.type || 'line', - mode: stacked ? 'stacked' : 'normal', - interpolate: interpolate, - drawLinesBetweenPoints: config.drawLinesBetweenPoints, - showCircles: config.showCircles, - radiusRatio: config.radiusRatio, - }; -}; - -export { updateVisualizationConfig }; diff --git a/src/plugins/visualizations/public/legacy/vis_update_state.js b/src/plugins/visualizations/public/legacy/vis_update_state.js index 45610701e08c8..e345b9e5b8c9a 100644 --- a/src/plugins/visualizations/public/legacy/vis_update_state.js +++ b/src/plugins/visualizations/public/legacy/vis_update_state.js @@ -75,6 +75,77 @@ function convertDateHistogramScaleMetrics(visState) { } } +function convertSeriesParams(visState) { + if (visState.params.seriesParams) { + return; + } + + // update value axis options + const isUserDefinedYAxis = visState.params.setYExtents; + const defaultYExtents = visState.params.defaultYExtents; + const mode = ['stacked', 'overlap'].includes(visState.params.mode) + ? 'normal' + : visState.params.mode || 'normal'; + + if (!visState.params.valueAxes || !visState.params.valueAxes.length) { + visState.params.valueAxes = [ + { + id: 'ValueAxis-1', + name: 'LeftAxis-1', + type: 'value', + position: 'left', + show: true, + style: {}, + scale: { + type: 'linear', + mode: 'normal', + }, + labels: { + show: true, + rotate: 0, + filter: false, + truncate: 100, + }, + title: { + text: 'Count', + }, + }, + ]; + } + + visState.params.valueAxes[0].scale = { + ...visState.params.valueAxes[0].scale, + type: visState.params.scale || 'linear', + setYExtents: visState.params.setYExtents || false, + defaultYExtents: visState.params.defaultYExtents || false, + boundsMargin: defaultYExtents ? visState.params.boundsMargin : 0, + min: isUserDefinedYAxis ? visState.params.yAxis.min : undefined, + max: isUserDefinedYAxis ? visState.params.yAxis.max : undefined, + mode: mode, + }; + + // update series options + const interpolate = visState.params.smoothLines ? 'cardinal' : visState.params.interpolate; + const stacked = ['stacked', 'percentage', 'wiggle', 'silhouette'].includes(visState.params.mode); + visState.params.seriesParams = [ + { + show: true, + type: visState.params.type || 'line', + mode: stacked ? 'stacked' : 'normal', + interpolate: interpolate, + drawLinesBetweenPoints: visState.params.drawLinesBetweenPoints, + showCircles: visState.params.showCircles, + radiusRatio: visState.params.radiusRatio, + data: { + label: 'Count', + id: '1', + }, + lineWidth: 2, + valueAxis: 'ValueAxis-1', + }, + ]; +} + /** * This function is responsible for updating old visStates - the actual saved object * object - into the format, that will be required by the current Kibana version. @@ -90,6 +161,10 @@ export const updateOldState = visState => { convertPropertyNames(newState); convertDateHistogramScaleMetrics(newState); + if (visState.params && ['line', 'area', 'histogram'].includes(visState.params.type)) { + convertSeriesParams(newState); + } + if (visState.type === 'gauge' && visState.fontSize) { delete newState.fontSize; _.set(newState, 'gauge.style.fontSize', visState.fontSize); diff --git a/src/plugins/visualizations/public/vis.ts b/src/plugins/visualizations/public/vis.ts index 3cab4faf2a27f..009dd71b9a912 100644 --- a/src/plugins/visualizations/public/vis.ts +++ b/src/plugins/visualizations/public/vis.ts @@ -29,8 +29,6 @@ import { isFunction, defaults, cloneDeep } from 'lodash'; import { PersistedState } from './persisted_state'; -// @ts-ignore -import { updateVisualizationConfig } from './legacy/vis_update'; import { getTypes, getAggs } from './services'; import { VisType } from './vis_types'; import { @@ -121,9 +119,6 @@ export class Vis { this.params = this.getParams(state.params); } - // move to migration script - updateVisualizationConfig(state.params, this.params); - if (state.data && state.data.searchSource) { this.data.searchSource = state.data.searchSource!; this.data.indexPattern = this.data.searchSource.getField('index'); From bb89b6a48cf63c18f95a3e119450041e309fd7e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Thu, 23 Apr 2020 12:16:49 +0100 Subject: [PATCH 25/39] [APM] change span property name from child_ids to child.id (#64209) --- .../waterfall_helpers/waterfall_helpers.test.ts | 4 ++-- .../Waterfall/waterfall_helpers/waterfall_helpers.ts | 10 +++++----- .../waterfallContainer.stories.data.ts | 2 +- x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts index d844ac8b5988d..e0a01e9422c85 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts @@ -191,7 +191,7 @@ describe('waterfall_helpers', () => { name: 'SELECT FROM products', id: 'mySpanIdB' }, - child_ids: ['mySpanIdA', 'mySpanIdC'] + child: { id: ['mySpanIdA', 'mySpanIdC'] } } as Span, { parent: { id: 'mySpanIdD' }, @@ -294,7 +294,7 @@ describe('waterfall_helpers', () => { name: 'SELECT FROM products', id: 'mySpanIdB' }, - child_ids: ['incorrectId', 'mySpanIdC'] + child: { id: ['incorrectId', 'mySpanIdC'] } } as Span, { parent: { id: 'mySpanIdD' }, diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts index 8a873b2ddf1c9..73193cc7c9dbb 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts @@ -237,7 +237,7 @@ const getWaterfallItems = (items: TraceAPIResponse['trace']['items']) => }); /** - * Changes the parent_id of items based on the child_ids property. + * Changes the parent_id of items based on the child.id property. * Solves the problem of Inferred spans that are created as child of trace spans * when it actually should be its parent. * @param waterfallItems @@ -245,10 +245,10 @@ const getWaterfallItems = (items: TraceAPIResponse['trace']['items']) => const reparentSpans = (waterfallItems: IWaterfallItem[]) => { return waterfallItems.map(waterfallItem => { if (waterfallItem.docType === 'span') { - const { child_ids: childIds } = waterfallItem.doc; - if (childIds) { - childIds.forEach(childId => { - const item = waterfallItems.find(_item => _item.id === childId); + const childId = waterfallItem.doc.child?.id; + if (childId) { + childId.forEach(id => { + const item = waterfallItems.find(_item => _item.id === id); if (item) { item.parentId = waterfallItem.id; } diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/waterfallContainer.stories.data.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/waterfallContainer.stories.data.ts index 306c8e4f3fedb..2f28e37f73f62 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/waterfallContainer.stories.data.ts +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/waterfallContainer.stories.data.ts @@ -2027,7 +2027,7 @@ export const inferredSpans = { id: '41226ae63af4f235', type: 'unknown' }, - child_ids: ['8d80de06aa11a6fc'] + child: { ids: ['8d80de06aa11a6fc'] } }, { container: { diff --git a/x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts b/x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts index e98b2f52089b3..f6c4fce76f134 100644 --- a/x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts +++ b/x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts @@ -52,5 +52,5 @@ export interface SpanRaw extends APMBaseDoc { id: string; }; observer?: Observer; - child_ids?: string[]; + child?: { id: string[] }; } From ebf55030b01f5fd5481347e9336cd73820332a5a Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 23 Apr 2020 13:49:07 +0100 Subject: [PATCH 26/39] fix(NA): node module main field resolution for new platform (#63886) * fix(NA): node module main field resolution for new platform * chore(NA): remove browserify from kbn optimizer main fields Co-authored-by: Elastic Machine --- packages/kbn-optimizer/src/worker/webpack.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index bb58bd0a3f005..9010746f9fde3 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -289,6 +289,7 @@ export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) { resolve: { extensions: ['.js', '.ts', '.tsx', '.json'], + mainFields: ['browser', 'main'], alias: { tinymath: require.resolve('tinymath/lib/tinymath.es5.js'), }, From e3442b1b75fdfec002ebbca4a6b61851f812535e Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Thu, 23 Apr 2020 08:55:33 -0400 Subject: [PATCH 27/39] [Ingest] Encrypt secrets in fleet saved objects (#63982) --- .../ingest_manager/common/constants/output.ts | 1 - .../common/types/models/agent.ts | 10 ++- .../plugins/ingest_manager/server/plugin.ts | 10 ++- .../ingest_manager/server/saved_objects.ts | 67 +++++++++++++++++-- .../server/services/agents/acks.test.ts | 23 +++++++ .../server/services/agents/actions.test.ts | 8 +-- .../server/services/agents/actions.ts | 51 +++++++++++--- .../server/services/agents/checkin.test.ts | 12 ++-- .../server/services/agents/checkin.ts | 17 +++-- .../server/services/agents/saved_objects.ts | 1 + .../server/services/agents/unenroll.ts | 7 +- .../services/api_keys/enrollment_api_key.ts | 20 ++++-- .../server/services/app_context.ts | 3 + .../apis/fleet/unenroll_agent.ts | 1 + 14 files changed, 181 insertions(+), 50 deletions(-) diff --git a/x-pack/plugins/ingest_manager/common/constants/output.ts b/x-pack/plugins/ingest_manager/common/constants/output.ts index 6060a2b63fc8e..4c22d0e3fe7a3 100644 --- a/x-pack/plugins/ingest_manager/common/constants/output.ts +++ b/x-pack/plugins/ingest_manager/common/constants/output.ts @@ -12,5 +12,4 @@ export const DEFAULT_OUTPUT = { is_default: true, type: OutputType.Elasticsearch, hosts: [''], - api_key: '', }; diff --git a/x-pack/plugins/ingest_manager/common/types/models/agent.ts b/x-pack/plugins/ingest_manager/common/types/models/agent.ts index 14b2b2e47d17f..fcd3955f3a32f 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/agent.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/agent.ts @@ -20,15 +20,18 @@ export interface NewAgentAction { sent_at?: string; } -export type AgentAction = NewAgentAction & { +export interface AgentAction extends NewAgentAction { id: string; agent_id: string; created_at: string; -} & SavedObjectAttributes; +} -export interface AgentActionSOAttributes extends NewAgentAction, SavedObjectAttributes { +export interface AgentActionSOAttributes extends SavedObjectAttributes { + type: 'CONFIG_CHANGE' | 'DATA_DUMP' | 'RESUME' | 'PAUSE'; + sent_at?: string; created_at: string; agent_id: string; + data?: string; } export interface AgentEvent { @@ -64,6 +67,7 @@ interface AgentBase { shared_id?: string; access_api_key_id?: string; default_api_key?: string; + default_api_key_id?: string; config_id?: string; config_revision?: number | null; config_newest_revision?: number; diff --git a/x-pack/plugins/ingest_manager/server/plugin.ts b/x-pack/plugins/ingest_manager/server/plugin.ts index 55b1d2961bca1..c07d2efba6756 100644 --- a/x-pack/plugins/ingest_manager/server/plugin.ts +++ b/x-pack/plugins/ingest_manager/server/plugin.ts @@ -13,7 +13,10 @@ import { SavedObjectsServiceStart, } from 'kibana/server'; import { LicensingPluginSetup } from '../../licensing/server'; -import { EncryptedSavedObjectsPluginStart } from '../../encrypted_saved_objects/server'; +import { + EncryptedSavedObjectsPluginStart, + EncryptedSavedObjectsPluginSetup, +} from '../../encrypted_saved_objects/server'; import { SecurityPluginSetup } from '../../security/server'; import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { @@ -26,7 +29,7 @@ import { AGENT_EVENT_SAVED_OBJECT_TYPE, ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, } from './constants'; - +import { registerEncryptedSavedObjects } from './saved_objects'; import { registerEPMRoutes, registerDatasourceRoutes, @@ -46,6 +49,7 @@ export interface IngestManagerSetupDeps { licensing: LicensingPluginSetup; security?: SecurityPluginSetup; features?: FeaturesPluginSetup; + encryptedSavedObjects: EncryptedSavedObjectsPluginSetup; } export type IngestManagerStartDeps = object; @@ -97,6 +101,8 @@ export class IngestManagerPlugin this.security = deps.security; } + registerEncryptedSavedObjects(deps.encryptedSavedObjects); + // Register feature // TODO: Flesh out privileges if (deps.features) { diff --git a/x-pack/plugins/ingest_manager/server/saved_objects.ts b/x-pack/plugins/ingest_manager/server/saved_objects.ts index 0a7229b1f2807..37a00228443e1 100644 --- a/x-pack/plugins/ingest_manager/server/saved_objects.ts +++ b/x-pack/plugins/ingest_manager/server/saved_objects.ts @@ -13,6 +13,7 @@ import { AGENT_ACTION_SAVED_OBJECT_TYPE, ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, } from './constants'; +import { EncryptedSavedObjectsPluginSetup } from '../../encrypted_saved_objects/server'; /* * Saved object mappings @@ -35,7 +36,7 @@ export const savedObjectMappings = { last_checkin: { type: 'date' }, config_revision: { type: 'integer' }, config_newest_revision: { type: 'integer' }, - // FIXME_INGEST https://github.com/elastic/kibana/issues/56554 + default_api_key_id: { type: 'keyword' }, default_api_key: { type: 'keyword' }, updated_at: { type: 'date' }, current_error_events: { type: 'text' }, @@ -45,8 +46,7 @@ export const savedObjectMappings = { properties: { agent_id: { type: 'keyword' }, type: { type: 'keyword' }, - // FIXME_INGEST https://github.com/elastic/kibana/issues/56554 - data: { type: 'flattened' }, + data: { type: 'binary' }, sent_at: { type: 'date' }, created_at: { type: 'date' }, }, @@ -83,7 +83,6 @@ export const savedObjectMappings = { properties: { name: { type: 'keyword' }, type: { type: 'keyword' }, - // FIXME_INGEST https://github.com/elastic/kibana/issues/56554 api_key: { type: 'binary' }, api_key_id: { type: 'keyword' }, config_id: { type: 'keyword' }, @@ -100,8 +99,6 @@ export const savedObjectMappings = { is_default: { type: 'boolean' }, hosts: { type: 'keyword' }, ca_sha256: { type: 'keyword' }, - // FIXME_INGEST https://github.com/elastic/kibana/issues/56554 - api_key: { type: 'keyword' }, fleet_enroll_username: { type: 'binary' }, fleet_enroll_password: { type: 'binary' }, config: { type: 'flattened' }, @@ -165,3 +162,61 @@ export const savedObjectMappings = { }, }, }; + +export function registerEncryptedSavedObjects( + encryptedSavedObjects: EncryptedSavedObjectsPluginSetup +) { + // Encrypted saved objects + encryptedSavedObjects.registerType({ + type: ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, + attributesToEncrypt: new Set(['api_key']), + attributesToExcludeFromAAD: new Set([ + 'name', + 'type', + 'api_key_id', + 'config_id', + 'created_at', + 'updated_at', + 'expire_at', + 'active', + ]), + }); + encryptedSavedObjects.registerType({ + type: OUTPUT_SAVED_OBJECT_TYPE, + attributesToEncrypt: new Set(['fleet_enroll_username', 'fleet_enroll_password']), + attributesToExcludeFromAAD: new Set([ + 'name', + 'type', + 'is_default', + 'hosts', + 'ca_sha256', + 'config', + ]), + }); + encryptedSavedObjects.registerType({ + type: AGENT_SAVED_OBJECT_TYPE, + attributesToEncrypt: new Set(['default_api_key']), + attributesToExcludeFromAAD: new Set([ + 'shared_id', + 'type', + 'active', + 'enrolled_at', + 'access_api_key_id', + 'version', + 'user_provided_metadata', + 'local_metadata', + 'config_id', + 'last_updated', + 'last_checkin', + 'config_revision', + 'config_newest_revision', + 'updated_at', + 'current_error_events', + ]), + }); + encryptedSavedObjects.registerType({ + type: AGENT_ACTION_SAVED_OBJECT_TYPE, + attributesToEncrypt: new Set(['data']), + attributesToExcludeFromAAD: new Set(['agent_id', 'type', 'sent_at', 'created_at']), + }); +} diff --git a/x-pack/plugins/ingest_manager/server/services/agents/acks.test.ts b/x-pack/plugins/ingest_manager/server/services/agents/acks.test.ts index b4c1f09015a69..62871710d9877 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/acks.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/acks.test.ts @@ -6,6 +6,8 @@ import Boom from 'boom'; import { SavedObjectsBulkResponse } from 'kibana/server'; import { savedObjectsClientMock } from '../../../../../../src/core/server/saved_objects/service/saved_objects_client.mock'; +import { encryptedSavedObjectsMock } from '../../../../../plugins/encrypted_saved_objects/server/mocks'; + import { Agent, AgentAction, @@ -14,10 +16,31 @@ import { } from '../../../common/types/models'; import { AGENT_TYPE_PERMANENT } from '../../../common/constants'; import { acknowledgeAgentActions } from './acks'; +import { appContextService } from '../app_context'; +import { IngestManagerAppContext } from '../../plugin'; describe('test agent acks services', () => { it('should succeed on valid and matched actions', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); + const mockStartEncryptedSOClient = encryptedSavedObjectsMock.createStart(); + appContextService.start(({ + encryptedSavedObjects: mockStartEncryptedSOClient, + } as unknown) as IngestManagerAppContext); + + mockStartEncryptedSOClient.getDecryptedAsInternalUser.mockReturnValue( + Promise.resolve({ + id: 'action1', + references: [], + type: 'agent_actions', + attributes: { + type: 'CONFIG_CHANGE', + agent_id: 'id', + sent_at: '2020-03-14T19:45:02.620Z', + timestamp: '2019-01-04T14:32:03.36764-05:00', + created_at: '2020-03-14T19:45:02.620Z', + }, + }) + ); mockSavedObjectsClient.bulkGet.mockReturnValue( Promise.resolve({ diff --git a/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts b/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts index f2e671c6dbaa8..29143502247aa 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts @@ -6,17 +6,17 @@ import { createAgentAction } from './actions'; import { SavedObject } from 'kibana/server'; -import { AgentAction, AgentActionSOAttributes } from '../../../common/types/models'; +import { AgentAction } from '../../../common/types/models'; import { savedObjectsClientMock } from '../../../../../../src/core/server/saved_objects/service/saved_objects_client.mock'; describe('test agent actions services', () => { it('should create a new action', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); - const newAgentAction: AgentActionSOAttributes = { + const newAgentAction: Omit = { agent_id: 'agentid', type: 'CONFIG_CHANGE', - data: 'data', + data: { content: 'data' }, sent_at: '2020-03-14T19:45:02.620Z', created_at: '2020-03-14T19:45:02.620Z', }; @@ -31,7 +31,7 @@ describe('test agent actions services', () => { .calls[0][1] as unknown) as AgentAction; expect(createdAction).toBeDefined(); expect(createdAction?.type).toEqual(newAgentAction.type); - expect(createdAction?.data).toEqual(newAgentAction.data); + expect(createdAction?.data).toEqual(JSON.stringify(newAgentAction.data)); expect(createdAction?.sent_at).toEqual(newAgentAction.sent_at); }); }); diff --git a/x-pack/plugins/ingest_manager/server/services/agents/actions.ts b/x-pack/plugins/ingest_manager/server/services/agents/actions.ts index a8ef0820f8d9f..1bb177e54282d 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/actions.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/actions.ts @@ -8,16 +8,21 @@ import { SavedObjectsClientContract } from 'kibana/server'; import { Agent, AgentAction, AgentActionSOAttributes } from '../../../common/types/models'; import { AGENT_ACTION_SAVED_OBJECT_TYPE } from '../../../common/constants'; import { savedObjectToAgentAction } from './saved_objects'; +import { appContextService } from '../app_context'; export async function createAgentAction( soClient: SavedObjectsClientContract, - newAgentAction: AgentActionSOAttributes + newAgentAction: Omit ): Promise { const so = await soClient.create(AGENT_ACTION_SAVED_OBJECT_TYPE, { ...newAgentAction, + data: newAgentAction.data ? JSON.stringify(newAgentAction.data) : undefined, }); - return savedObjectToAgentAction(so); + const agentAction = savedObjectToAgentAction(so); + agentAction.data = newAgentAction.data; + + return agentAction; } export async function getAgentActionsForCheckin( @@ -29,21 +34,47 @@ export async function getAgentActionsForCheckin( filter: `not ${AGENT_ACTION_SAVED_OBJECT_TYPE}.attributes.sent_at: * and ${AGENT_ACTION_SAVED_OBJECT_TYPE}.attributes.agent_id:${agentId}`, }); - return res.saved_objects.map(savedObjectToAgentAction); + return Promise.all( + res.saved_objects.map(async so => { + // Get decrypted actions + return savedObjectToAgentAction( + await appContextService + .getEncryptedSavedObjects() + .getDecryptedAsInternalUser( + AGENT_ACTION_SAVED_OBJECT_TYPE, + so.id + ) + ); + }) + ); } export async function getAgentActionByIds( soClient: SavedObjectsClientContract, actionIds: string[] ) { - const res = await soClient.bulkGet( - actionIds.map(actionId => ({ - id: actionId, - type: AGENT_ACTION_SAVED_OBJECT_TYPE, - })) - ); + const actions = ( + await soClient.bulkGet( + actionIds.map(actionId => ({ + id: actionId, + type: AGENT_ACTION_SAVED_OBJECT_TYPE, + })) + ) + ).saved_objects.map(savedObjectToAgentAction); - return res.saved_objects.map(savedObjectToAgentAction); + return Promise.all( + actions.map(async action => { + // Get decrypted actions + return savedObjectToAgentAction( + await appContextService + .getEncryptedSavedObjects() + .getDecryptedAsInternalUser( + AGENT_ACTION_SAVED_OBJECT_TYPE, + action.id + ) + ); + }) + ); } export interface ActionsService { diff --git a/x-pack/plugins/ingest_manager/server/services/agents/checkin.test.ts b/x-pack/plugins/ingest_manager/server/services/agents/checkin.test.ts index ec10ca6e77e05..72a86d7c8158e 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/checkin.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/checkin.test.ts @@ -53,12 +53,12 @@ describe('Agent checkin service', () => { agent_id: 'agent1', type: 'CONFIG_CHANGE', created_at: new Date().toISOString(), - data: JSON.stringify({ + data: { config: { id: 'config1', revision: 2, }, - }), + }, }, ] ); @@ -80,24 +80,24 @@ describe('Agent checkin service', () => { agent_id: 'agent1', type: 'CONFIG_CHANGE', created_at: new Date().toISOString(), - data: JSON.stringify({ + data: { config: { id: 'config2', revision: 2, }, - }), + }, }, { id: 'action1', agent_id: 'agent1', type: 'CONFIG_CHANGE', created_at: new Date().toISOString(), - data: JSON.stringify({ + data: { config: { id: 'config1', revision: 1, }, - }), + }, }, ] ); diff --git a/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts b/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts index 2873aad7f691a..c96a81ed9b758 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts @@ -17,6 +17,7 @@ import { agentConfigService } from '../agent_config'; import * as APIKeysService from '../api_keys'; import { AGENT_SAVED_OBJECT_TYPE, AGENT_EVENT_SAVED_OBJECT_TYPE } from '../../constants'; import { getAgentActionsForCheckin, createAgentAction } from './actions'; +import { appContextService } from '../app_context'; export async function agentCheckin( soClient: SavedObjectsClientContract, @@ -27,7 +28,6 @@ export async function agentCheckin( const updateData: { last_checkin: string; default_api_key?: string; - actions?: AgentAction[]; local_metadata?: string; current_error_events?: string; } = { @@ -38,11 +38,17 @@ export async function agentCheckin( // Generate new agent config if config is updated if (agent.config_id && shouldCreateConfigAction(agent, actions)) { + const { + attributes: { default_api_key: defaultApiKey }, + } = await appContextService + .getEncryptedSavedObjects() + .getDecryptedAsInternalUser(AGENT_SAVED_OBJECT_TYPE, agent.id); + const config = await agentConfigService.getFullConfig(soClient, agent.config_id); if (config) { // Assign output API keys // We currently only support default ouput - if (!agent.default_api_key) { + if (!defaultApiKey) { updateData.default_api_key = await APIKeysService.generateOutputApiKey( soClient, 'default', @@ -50,7 +56,7 @@ export async function agentCheckin( ); } // Mutate the config to set the api token for this agent - config.outputs.default.api_key = agent.default_api_key || updateData.default_api_key; + config.outputs.default.api_key = defaultApiKey || updateData.default_api_key; const configChangeAction = await createAgentAction(soClient, { agent_id: agent.id, @@ -62,9 +68,6 @@ export async function agentCheckin( actions.push(configChangeAction); } } - if (localMetadata) { - updateData.local_metadata = JSON.stringify(localMetadata); - } const { updatedErrorEvents } = await processEventsForCheckin(soClient, agent, events); @@ -172,7 +175,7 @@ export function shouldCreateConfigAction(agent: Agent, actions: AgentAction[]): return false; } - const data = JSON.parse(action.data); + const { data } = action; return ( data.config.id === agent.config_id && data.config.revision === agent.config_newest_revision diff --git a/x-pack/plugins/ingest_manager/server/services/agents/saved_objects.ts b/x-pack/plugins/ingest_manager/server/services/agents/saved_objects.ts index aa88520740687..b182662e0fb4e 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/saved_objects.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/saved_objects.ts @@ -38,5 +38,6 @@ export function savedObjectToAgentAction(so: SavedObject(AGENT_SAVED_OBJECT_TYPE, agentId, { diff --git a/x-pack/plugins/ingest_manager/server/services/api_keys/enrollment_api_key.ts b/x-pack/plugins/ingest_manager/server/services/api_keys/enrollment_api_key.ts index a6a2db8be4e9d..c9ead09b0908d 100644 --- a/x-pack/plugins/ingest_manager/server/services/api_keys/enrollment_api_key.ts +++ b/x-pack/plugins/ingest_manager/server/services/api_keys/enrollment_api_key.ts @@ -10,6 +10,7 @@ import { EnrollmentAPIKey, EnrollmentAPIKeySOAttributes } from '../../types'; import { ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE } from '../../constants'; import { createAPIKey, invalidateAPIKey } from './security'; import { agentConfigService } from '../agent_config'; +import { appContextService } from '../app_context'; export async function listEnrollmentApiKeys( soClient: SavedObjectsClientContract, @@ -45,9 +46,13 @@ export async function listEnrollmentApiKeys( } export async function getEnrollmentAPIKey(soClient: SavedObjectsClientContract, id: string) { - return savedObjectToEnrollmentApiKey( - await soClient.get(ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, id) - ); + const so = await appContextService + .getEncryptedSavedObjects() + .getDecryptedAsInternalUser( + ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, + id + ); + return savedObjectToEnrollmentApiKey(so); } /** @@ -120,16 +125,19 @@ export async function generateEnrollmentAPIKey( const apiKey = Buffer.from(`${key.id}:${key.api_key}`).toString('base64'); - return savedObjectToEnrollmentApiKey( - await soClient.create(ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, { + const so = await soClient.create( + ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, + { active: true, api_key_id: key.id, api_key: apiKey, name, config_id: configId, created_at: new Date().toISOString(), - }) + } ); + + return getEnrollmentAPIKey(soClient, so.id); } function savedObjectToEnrollmentApiKey({ diff --git a/x-pack/plugins/ingest_manager/server/services/app_context.ts b/x-pack/plugins/ingest_manager/server/services/app_context.ts index a0a7c8dd7c05a..e917d2edd1309 100644 --- a/x-pack/plugins/ingest_manager/server/services/app_context.ts +++ b/x-pack/plugins/ingest_manager/server/services/app_context.ts @@ -34,6 +34,9 @@ class AppContextService { public stop() {} public getEncryptedSavedObjects() { + if (!this.encryptedSavedObjects) { + throw new Error('Encrypted saved object start service not set.'); + } return this.encryptedSavedObjects; } diff --git a/x-pack/test/api_integration/apis/fleet/unenroll_agent.ts b/x-pack/test/api_integration/apis/fleet/unenroll_agent.ts index 2acfca63995f1..d33b92acf95a5 100644 --- a/x-pack/test/api_integration/apis/fleet/unenroll_agent.ts +++ b/x-pack/test/api_integration/apis/fleet/unenroll_agent.ts @@ -44,6 +44,7 @@ export default function(providerContext: FtrProviderContext) { }); // @ts-ignore agentDoc.agents.access_api_key_id = accessAPIKeyId; + agentDoc.agents.default_api_key_id = outputAPIKeyBody.id; agentDoc.agents.default_api_key = Buffer.from( `${outputAPIKeyBody.id}:${outputAPIKeyBody.api_key}` ).toString('base64'); From 7acfbab9b42798cc559bec0de2db3a0fbbeac74b Mon Sep 17 00:00:00 2001 From: Kaarina Tungseth Date: Thu, 23 Apr 2020 09:03:31 -0500 Subject: [PATCH 28/39] [DOCS] Removed supported aggregations links from visualization pages (#64244) --- docs/visualize/color-picker.asciidoc | 4 -- docs/visualize/lens.asciidoc | 14 ------ docs/visualize/most-frequent.asciidoc | 14 ------ docs/visualize/tilemap.asciidoc | 13 ------ docs/visualize/tsvb.asciidoc | 12 ------ docs/visualize/visualize_rollup_data.asciidoc | 43 ------------------- 6 files changed, 100 deletions(-) delete mode 100644 docs/visualize/color-picker.asciidoc delete mode 100644 docs/visualize/visualize_rollup_data.asciidoc diff --git a/docs/visualize/color-picker.asciidoc b/docs/visualize/color-picker.asciidoc deleted file mode 100644 index e0f23262068d3..0000000000000 --- a/docs/visualize/color-picker.asciidoc +++ /dev/null @@ -1,4 +0,0 @@ -You can customize the colors of your visualization by clicking the color dot next to each label to display the -_color picker_. - -image::images/color-picker.png[An array of color dots that users can select] diff --git a/docs/visualize/lens.asciidoc b/docs/visualize/lens.asciidoc index b181763c0d0d0..422afbb201183 100644 --- a/docs/visualize/lens.asciidoc +++ b/docs/visualize/lens.asciidoc @@ -14,20 +14,6 @@ beta[] * Save your visualization for use in a dashboard. -[float] -[[lens-aggregation]] -=== Supported aggregations - -Lens supports the following aggregations: - -* <> - -* <> - -* <> - -* <> - [float] [[drag-drop]] === Drag and drop diff --git a/docs/visualize/most-frequent.asciidoc b/docs/visualize/most-frequent.asciidoc index ba291e3cc6859..f716930e7e65c 100644 --- a/docs/visualize/most-frequent.asciidoc +++ b/docs/visualize/most-frequent.asciidoc @@ -13,20 +13,6 @@ The most frequently used visualizations include: [[metric-chart]] -[float] -[[frequently-used-viz-aggregation]] -=== Supported aggregations - -The most frequently used visualizations support the following aggregations: - -* <> - -* <> - -* <> - -* <> - [float] === Configure your visualization diff --git a/docs/visualize/tilemap.asciidoc b/docs/visualize/tilemap.asciidoc index 51342847080e0..c889bd0bb6ca0 100644 --- a/docs/visualize/tilemap.asciidoc +++ b/docs/visualize/tilemap.asciidoc @@ -6,19 +6,6 @@ Display graphical representations of data where the individual values are repres [role="screenshot"] image::images/visualize_heat_map_example.png[] -[float] -[[build-heat-map]] -=== Build a heat map - -To display your data on the heat map, use the supported aggregations. - -Heat maps support the following aggregations: - -* <> -* <> -* <> -* <> - [float] [[navigate-heatmap]] === Change the color ranges diff --git a/docs/visualize/tsvb.asciidoc b/docs/visualize/tsvb.asciidoc index 69d6985acd1e4..36709c2cc6437 100644 --- a/docs/visualize/tsvb.asciidoc +++ b/docs/visualize/tsvb.asciidoc @@ -43,18 +43,6 @@ Table:: Display data from multiple time series by defining the field group to sh [role="screenshot"] image:images/tsvb-table.png["Table visualization"] -[float] -[[tsvb-aggregation]] -=== Supported aggregations - -TSVB supports the following aggregations: - -* <> - -* <> - -* <> - [float] [[create-tsvb-visualization]] === Create TSVB visualizations diff --git a/docs/visualize/visualize_rollup_data.asciidoc b/docs/visualize/visualize_rollup_data.asciidoc deleted file mode 100644 index 481cbc6e39418..0000000000000 --- a/docs/visualize/visualize_rollup_data.asciidoc +++ /dev/null @@ -1,43 +0,0 @@ -[role="xpack"] -[[visualize-rollup-data]] -== Use rolled up data in a visualization - -beta[] - -You can visualize your rolled up data in a variety of charts, tables, maps, and -more. Most visualizations support rolled up data, with the exception of -Timelion and Vega visualizations. - -To get started, go to *Management > Kibana > Index patterns.* -If a rollup index is detected in the cluster, *Create index pattern* -includes an item for creating a rollup index pattern. - -[role="screenshot"] -image::images/management_create_rollup_menu.png[Create index pattern menu] - -You can match an index pattern to only rolled up data, or mix both rolled up -and raw data to visualize all data together. An index pattern can match only one -rolled up index, not multiple. There is no restriction on the number of standard -indices that an index pattern can match. When matching multiple indices, -use a comma to separate the names, with no space after the comma. - -Keep the following in mind when creating a visualization from rolled up data: - -* The data in a rollup index only has summarized metrics for specific fields. -You can’t search any other field from the original raw data. -* Data is summarized into time buckets that might be split into sub buckets for -numeric field values or terms. You can ask for a time aggregation that takes -several time buckets and combines them to lower granularity. For example, -if the rollup job was aggregated by hours, you can ask for buckets of days. - -The following visualization of rolled up data shows the date histogram -interval multiple and the limited metrics aggregations. - -[role="screenshot"] -image::images/management_rollups_visualization.png[][Rollups in visualizations] - -Dashboards can have a mixture of rollup visualizations and regular visualizations, -as shown in the following figure. Note that not all queries and filters support rollups. - -[role="screenshot"] -image::images/management_rolled_dashboard.png[][Rollups in dashboards] From 46901f557b11e3e53256c44534e579712d2e18e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Thu, 23 Apr 2020 15:24:00 +0100 Subject: [PATCH 29/39] [APM] Mark an Agent configuration as applied without etag attribute. (#63967) * adding new attribute to mark config as applied * changing query parameter name * changing property name * refactoring * updating test description --- .../routes/settings/agent_configuration.ts | 17 ++++++-- .../apis/apm/agent_configuration.ts | 40 +++++++++++++++++-- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts b/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts index 8cfd736a336c2..6268f5899d7ff 100644 --- a/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts +++ b/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts @@ -143,11 +143,15 @@ export const agentConfigurationSearchRoute = createRoute(core => ({ params: { body: t.intersection([ t.type({ service: serviceRt }), - t.partial({ etag: t.string }) + t.partial({ etag: t.string, mark_as_applied_by_agent: t.boolean }) ]) }, handler: async ({ context, request }) => { - const { service, etag } = context.params.body; + const { + service, + etag, + mark_as_applied_by_agent: markAsAppliedByAgent + } = context.params.body; const setup = await setupRequest(context, request); const config = await searchConfigurations({ @@ -166,9 +170,14 @@ export const agentConfigurationSearchRoute = createRoute(core => ({ `Config was found for ${service.name}/${service.environment}` ); - // update `applied_by_agent` field if etags match + // update `applied_by_agent` field + // when `markAsAppliedByAgent` is true (Jaeger agent doesn't have etags) + // or if etags match. // this happens in the background and doesn't block the response - if (etag === config._source.etag && !config._source.applied_by_agent) { + if ( + (markAsAppliedByAgent || etag === config._source.etag) && + !config._source.applied_by_agent + ) { markAppliedByAgent({ id: config._id, body: config._source, setup }); } diff --git a/x-pack/test/api_integration/apis/apm/agent_configuration.ts b/x-pack/test/api_integration/apis/apm/agent_configuration.ts index 41d78995711f2..8af648e062cf4 100644 --- a/x-pack/test/api_integration/apis/apm/agent_configuration.ts +++ b/x-pack/test/api_integration/apis/apm/agent_configuration.ts @@ -182,15 +182,21 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte service: { name: 'myservice', environment: 'development' }, settings: { transaction_sample_rate: '0.9' }, }; + const configProduction = { + service: { name: 'myservice', environment: 'production' }, + settings: { transaction_sample_rate: '0.9' }, + }; let etag: string; before(async () => { log.debug('creating agent configuration'); await createConfiguration(config); + await createConfiguration(configProduction); }); after(async () => { await deleteConfiguration(config); + await deleteConfiguration(configProduction); }); it(`should have 'applied_by_agent=false' before supplying etag`, async () => { @@ -210,17 +216,45 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte }); it(`should have 'applied_by_agent=true' after supplying etag`, async () => { - async function getAppliedByAgent() { + await searchConfigurations({ + service: { name: 'myservice', environment: 'development' }, + etag, + }); + + async function hasBeenAppliedByAgent() { const { body } = await searchConfigurations({ service: { name: 'myservice', environment: 'development' }, - etag, }); return body._source.applied_by_agent; } // wait until `applied_by_agent` has been updated in elasticsearch - expect(await waitFor(getAppliedByAgent)).to.be(true); + expect(await waitFor(hasBeenAppliedByAgent)).to.be(true); + }); + it(`should have 'applied_by_agent=false' before marking as applied`, async () => { + const res1 = await searchConfigurations({ + service: { name: 'myservice', environment: 'production' }, + }); + + expect(res1.body._source.applied_by_agent).to.be(false); + }); + it(`should have 'applied_by_agent=true' when 'mark_as_applied_by_agent' attribute is true`, async () => { + await searchConfigurations({ + service: { name: 'myservice', environment: 'production' }, + mark_as_applied_by_agent: true, + }); + + async function hasBeenAppliedByAgent() { + const { body } = await searchConfigurations({ + service: { name: 'myservice', environment: 'production' }, + }); + + return body._source.applied_by_agent; + } + + // wait until `applied_by_agent` has been updated in elasticsearch + expect(await waitFor(hasBeenAppliedByAgent)).to.be(true); }); }); }); From f7e1c17f28128deecbc3350350098ec57850d0eb Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Thu, 23 Apr 2020 07:53:54 -0700 Subject: [PATCH 30/39] Implemented do not return the config from the action APIs get and getAll (#64266) --- .../actions/server/actions_client.test.ts | 6 --- .../plugins/actions/server/actions_client.ts | 2 - x-pack/plugins/actions/server/types.ts | 2 +- .../security_and_spaces/tests/actions/get.ts | 3 -- .../tests/actions/get_all.ts | 48 ------------------- .../spaces_only/tests/actions/get.ts | 3 -- .../spaces_only/tests/actions/get_all.ts | 32 ------------- 7 files changed, 1 insertion(+), 95 deletions(-) diff --git a/x-pack/plugins/actions/server/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client.test.ts index 955e1569380a5..14441bfd52dd7 100644 --- a/x-pack/plugins/actions/server/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client.test.ts @@ -348,9 +348,6 @@ describe('get()', () => { actionTypeId: '.slack', isPreconfigured: true, name: 'test', - config: { - foo: 'bar', - }, }); expect(savedObjectsClient.get).not.toHaveBeenCalled(); }); @@ -418,9 +415,6 @@ describe('getAll()', () => { actionTypeId: '.slack', isPreconfigured: true, name: 'test', - config: { - foo: 'bar', - }, referencedByCount: 2, }, ]); diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts index 8f73bfb31ea4d..f52e293296955 100644 --- a/x-pack/plugins/actions/server/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client.ts @@ -152,7 +152,6 @@ export class ActionsClient { id, actionTypeId: preconfiguredActionsList.actionTypeId, name: preconfiguredActionsList.name, - config: preconfiguredActionsList.config, isPreconfigured: true, }; } @@ -184,7 +183,6 @@ export class ActionsClient { id: preconfiguredAction.id, actionTypeId: preconfiguredAction.actionTypeId, name: preconfiguredAction.name, - config: preconfiguredAction.config, isPreconfigured: true, })), ].sort((a, b) => a.name.localeCompare(b.name)); diff --git a/x-pack/plugins/actions/server/types.ts b/x-pack/plugins/actions/server/types.ts index 92e38d77314f8..088398b40830e 100644 --- a/x-pack/plugins/actions/server/types.ts +++ b/x-pack/plugins/actions/server/types.ts @@ -54,7 +54,7 @@ export interface ActionResult { id: string; actionTypeId: string; name: string; - config: Record; + config?: Record; isPreconfigured: boolean; } diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get.ts index c84b089d48c85..e43f9dba4b2dc 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get.ts @@ -141,9 +141,6 @@ export default function getActionTests({ getService }: FtrProviderContext) { actionTypeId: '.slack', name: 'Slack#xyz', isPreconfigured: true, - config: { - webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz', - }, }); break; default: diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get_all.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get_all.ts index 0b637326d4667..95b564e63d715 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get_all.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get_all.ts @@ -73,11 +73,6 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, actionTypeId: '.index', name: 'preconfigured_es_index_action', - config: { - index: 'functional-test-actions-index-preconfigured', - refresh: true, - executionTimeField: 'timestamp', - }, referencedByCount: 0, }, { @@ -85,9 +80,6 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, actionTypeId: '.slack', name: 'Slack#xyz', - config: { - webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz', - }, referencedByCount: 0, }, { @@ -95,11 +87,6 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, actionTypeId: 'system-abc-action-type', name: 'SystemABC', - config: { - xyzConfig1: 'value1', - xyzConfig2: 'value2', - listOfThings: ['a', 'b', 'c', 'd'], - }, referencedByCount: 0, }, { @@ -107,9 +94,6 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, actionTypeId: 'test.index-record', name: 'Test:_Preconfigured_Index_Record', - config: { - unencrypted: 'ignored-but-required', - }, referencedByCount: 0, }, ]); @@ -194,11 +178,6 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, actionTypeId: '.index', name: 'preconfigured_es_index_action', - config: { - index: 'functional-test-actions-index-preconfigured', - refresh: true, - executionTimeField: 'timestamp', - }, referencedByCount: 0, }, { @@ -206,9 +185,6 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, actionTypeId: '.slack', name: 'Slack#xyz', - config: { - webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz', - }, referencedByCount: 1, }, { @@ -216,11 +192,6 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, actionTypeId: 'system-abc-action-type', name: 'SystemABC', - config: { - xyzConfig1: 'value1', - xyzConfig2: 'value2', - listOfThings: ['a', 'b', 'c', 'd'], - }, referencedByCount: 0, }, { @@ -228,9 +199,6 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, actionTypeId: 'test.index-record', name: 'Test:_Preconfigured_Index_Record', - config: { - unencrypted: 'ignored-but-required', - }, referencedByCount: 0, }, ]); @@ -281,11 +249,6 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, actionTypeId: '.index', name: 'preconfigured_es_index_action', - config: { - index: 'functional-test-actions-index-preconfigured', - refresh: true, - executionTimeField: 'timestamp', - }, referencedByCount: 0, }, { @@ -293,9 +256,6 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, actionTypeId: '.slack', name: 'Slack#xyz', - config: { - webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz', - }, referencedByCount: 0, }, { @@ -303,11 +263,6 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, actionTypeId: 'system-abc-action-type', name: 'SystemABC', - config: { - xyzConfig1: 'value1', - xyzConfig2: 'value2', - listOfThings: ['a', 'b', 'c', 'd'], - }, referencedByCount: 0, }, { @@ -315,9 +270,6 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, actionTypeId: 'test.index-record', name: 'Test:_Preconfigured_Index_Record', - config: { - unencrypted: 'ignored-but-required', - }, referencedByCount: 0, }, ]); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts index a4a13441fb766..4eb8c16f4fb3a 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts @@ -79,9 +79,6 @@ export default function getActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, actionTypeId: '.slack', name: 'Slack#xyz', - config: { - webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz', - }, }); }); }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts index ec59e56b08308..62abdddc6a1bf 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts @@ -50,11 +50,6 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, actionTypeId: '.index', name: 'preconfigured_es_index_action', - config: { - index: 'functional-test-actions-index-preconfigured', - refresh: true, - executionTimeField: 'timestamp', - }, referencedByCount: 0, }, { @@ -62,9 +57,6 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, actionTypeId: '.slack', name: 'Slack#xyz', - config: { - webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz', - }, referencedByCount: 0, }, { @@ -72,11 +64,6 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, actionTypeId: 'system-abc-action-type', name: 'SystemABC', - config: { - xyzConfig1: 'value1', - xyzConfig2: 'value2', - listOfThings: ['a', 'b', 'c', 'd'], - }, referencedByCount: 0, }, { @@ -84,9 +71,6 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, actionTypeId: 'test.index-record', name: 'Test:_Preconfigured_Index_Record', - config: { - unencrypted: 'ignored-but-required', - }, referencedByCount: 0, }, ]); @@ -115,11 +99,6 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, actionTypeId: '.index', name: 'preconfigured_es_index_action', - config: { - index: 'functional-test-actions-index-preconfigured', - refresh: true, - executionTimeField: 'timestamp', - }, referencedByCount: 0, }, { @@ -127,9 +106,6 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, actionTypeId: '.slack', name: 'Slack#xyz', - config: { - webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz', - }, referencedByCount: 0, }, { @@ -137,11 +113,6 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, actionTypeId: 'system-abc-action-type', name: 'SystemABC', - config: { - xyzConfig1: 'value1', - xyzConfig2: 'value2', - listOfThings: ['a', 'b', 'c', 'd'], - }, referencedByCount: 0, }, { @@ -149,9 +120,6 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, actionTypeId: 'test.index-record', name: 'Test:_Preconfigured_Index_Record', - config: { - unencrypted: 'ignored-but-required', - }, referencedByCount: 0, }, ]); From 3e0907fabfdc8e4c8271145acbe28497bde5c41a Mon Sep 17 00:00:00 2001 From: Josh Dover Date: Thu, 23 Apr 2020 09:13:00 -0600 Subject: [PATCH 31/39] Use runtime publicPath for KP plugin bundles (#64226) --- .../basic_optimization.test.ts.snap | 4 +- .../src/worker/webpack.config.ts | 9 ++- packages/kbn-ui-shared-deps/index.d.ts | 5 ++ packages/kbn-ui-shared-deps/index.js | 1 + .../kbn-ui-shared-deps/public_path_loader.js | 23 ++++++ packages/kbn-ui-shared-deps/webpack.config.js | 12 ++- src/legacy/core_plugins/tests_bundle/index.js | 13 +++ .../ui/ui_render/bootstrap/template.js.hbs | 1 + src/legacy/ui/ui_render/ui_render_mixin.js | 14 ++++ src/optimize/bundles_route/bundles_route.js | 79 +++++++++++-------- .../bundles_route/dynamic_asset_response.js | 9 ++- tasks/config/karma.js | 2 + 12 files changed, 132 insertions(+), 40 deletions(-) create mode 100644 packages/kbn-ui-shared-deps/public_path_loader.js diff --git a/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap b/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap index 4b4bb1282d939..fe0f75c05c646 100644 --- a/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap +++ b/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap @@ -57,6 +57,6 @@ OptimizerConfig { } `; -exports[`builds expected bundles, saves bundle counts to metadata: bar bundle 1`] = `"var __kbnBundles__=typeof __kbnBundles__===\\"object\\"?__kbnBundles__:{};__kbnBundles__[\\"plugin/bar\\"]=function(modules){function webpackJsonpCallback(data){var chunkIds=data[0];var moreModules=data[1];var moduleId,chunkId,i=0,resolves=[];for(;i `/${bundle.type}:${bundle.id}/${Path.relative( bundle.sourceRoot, @@ -146,6 +144,13 @@ export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) { ], rules: [ + { + include: Path.join(bundle.contextDir, bundle.entry), + loader: UiSharedDeps.publicPathLoader, + options: { + key: bundle.id, + }, + }, { test: /\.css$/, include: /node_modules/, diff --git a/packages/kbn-ui-shared-deps/index.d.ts b/packages/kbn-ui-shared-deps/index.d.ts index dec519da69641..b829c87d91c4a 100644 --- a/packages/kbn-ui-shared-deps/index.d.ts +++ b/packages/kbn-ui-shared-deps/index.d.ts @@ -53,3 +53,8 @@ export const lightCssDistFilename: string; export const externals: { [key: string]: string; }; + +/** + * Webpack loader for configuring the public path lookup from `window.__kbnPublicPath__`. + */ +export const publicPathLoader: string; diff --git a/packages/kbn-ui-shared-deps/index.js b/packages/kbn-ui-shared-deps/index.js index 666ec7a46ff06..42ed08259ac8f 100644 --- a/packages/kbn-ui-shared-deps/index.js +++ b/packages/kbn-ui-shared-deps/index.js @@ -64,3 +64,4 @@ exports.externals = { 'elasticsearch-browser': '__kbnSharedDeps__.ElasticsearchBrowser', 'elasticsearch-browser/elasticsearch': '__kbnSharedDeps__.ElasticsearchBrowser', }; +exports.publicPathLoader = require.resolve('./public_path_loader'); diff --git a/packages/kbn-ui-shared-deps/public_path_loader.js b/packages/kbn-ui-shared-deps/public_path_loader.js new file mode 100644 index 0000000000000..6b7a27c9ca52b --- /dev/null +++ b/packages/kbn-ui-shared-deps/public_path_loader.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 = function(source) { + const options = this.query; + return `__webpack_public_path__ = window.__kbnPublicPath__['${options.key}'];${source}`; +}; diff --git a/packages/kbn-ui-shared-deps/webpack.config.js b/packages/kbn-ui-shared-deps/webpack.config.js index a875274544905..bf63c57765859 100644 --- a/packages/kbn-ui-shared-deps/webpack.config.js +++ b/packages/kbn-ui-shared-deps/webpack.config.js @@ -46,7 +46,6 @@ exports.getWebpackConfig = ({ dev = false } = {}) => ({ path: UiSharedDeps.distDir, filename: '[name].js', sourceMapFilename: '[file].map', - publicPath: '__REPLACE_WITH_PUBLIC_PATH__', devtoolModuleFilenameTemplate: info => `kbn-ui-shared-deps/${Path.relative(REPO_ROOT, info.absoluteResourcePath)}`, library: '__kbnSharedDeps__', @@ -55,6 +54,17 @@ exports.getWebpackConfig = ({ dev = false } = {}) => ({ module: { noParse: [MOMENT_SRC], rules: [ + { + include: [require.resolve('./entry.js')], + use: [ + { + loader: UiSharedDeps.publicPathLoader, + options: { + key: 'kbn-ui-shared-deps', + }, + }, + ], + }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'], diff --git a/src/legacy/core_plugins/tests_bundle/index.js b/src/legacy/core_plugins/tests_bundle/index.js index 5e78047088d2a..e1966a9e8b266 100644 --- a/src/legacy/core_plugins/tests_bundle/index.js +++ b/src/legacy/core_plugins/tests_bundle/index.js @@ -148,6 +148,19 @@ export default kibana => { .type('text/css'); }, }); + + // Sets global variables normally set by the bootstrap.js script + kbnServer.server.route({ + path: '/test_bundle/karma/globals.js', + method: 'GET', + async handler(req, h) { + const basePath = config.get('server.basePath'); + + const file = `window.__kbnPublicPath__ = { 'kbn-ui-shared-deps': "${basePath}/bundles/kbn-ui-shared-deps/" };`; + + return h.response(file).header('content-type', 'application/json'); + }, + }); }, __globalImportAliases__: { diff --git a/src/legacy/ui/ui_render/bootstrap/template.js.hbs b/src/legacy/ui/ui_render/bootstrap/template.js.hbs index 7250fa4fc9eca..8a71c6ccb1506 100644 --- a/src/legacy/ui/ui_render/bootstrap/template.js.hbs +++ b/src/legacy/ui/ui_render/bootstrap/template.js.hbs @@ -1,6 +1,7 @@ var kbnCsp = JSON.parse(document.querySelector('kbn-csp').getAttribute('data')); window.__kbnStrictCsp__ = kbnCsp.strictCsp; window.__kbnDarkMode__ = {{darkMode}}; +window.__kbnPublicPath__ = {{publicPathMap}}; if (window.__kbnStrictCsp__ && window.__kbnCspNotEnforced__) { var legacyBrowserError = document.getElementById('kbn_legacy_browser_error'); diff --git a/src/legacy/ui/ui_render/ui_render_mixin.js b/src/legacy/ui/ui_render/ui_render_mixin.js index 1e84405dd5153..801eecf5b608b 100644 --- a/src/legacy/ui/ui_render/ui_render_mixin.js +++ b/src/legacy/ui/ui_render/ui_render_mixin.js @@ -153,11 +153,25 @@ export function uiRenderMixin(kbnServer, server, config) { `${regularBundlePath}/plugin/kibanaReact/kibanaReact.plugin.js`, ]; + const uiPluginIds = [...kbnServer.newPlatform.__internals.uiPlugins.public.keys()]; + + // These paths should align with the bundle routes configured in + // src/optimize/bundles_route/bundles_route.js + const publicPathMap = JSON.stringify({ + core: `${regularBundlePath}/core/`, + 'kbn-ui-shared-deps': `${regularBundlePath}/kbn-ui-shared-deps/`, + ...uiPluginIds.reduce( + (acc, pluginId) => ({ ...acc, [pluginId]: `${regularBundlePath}/plugin/${pluginId}/` }), + {} + ), + }); + const bootstrap = new AppBootstrap({ templateData: { darkMode, jsDependencyPaths, styleSheetPaths, + publicPathMap, entryBundlePath: isCore ? `${regularBundlePath}/core/core.entry.js` : `${regularBundlePath}/${app.getId()}.bundle.js`, diff --git a/src/optimize/bundles_route/bundles_route.js b/src/optimize/bundles_route/bundles_route.js index 530dabb9a46a3..4030988c8552c 100644 --- a/src/optimize/bundles_route/bundles_route.js +++ b/src/optimize/bundles_route/bundles_route.js @@ -72,43 +72,57 @@ export function createBundlesRoute({ } return [ - buildRouteForBundles( - `${basePublicPath}/bundles/kbn-ui-shared-deps/`, - '/bundles/kbn-ui-shared-deps/', - UiSharedDeps.distDir, - fileHashCache - ), + buildRouteForBundles({ + publicPath: `${basePublicPath}/bundles/kbn-ui-shared-deps/`, + routePath: '/bundles/kbn-ui-shared-deps/', + bundlesPath: UiSharedDeps.distDir, + fileHashCache, + replacePublicPath: false, + }), ...npUiPluginPublicDirs.map(({ id, path }) => - buildRouteForBundles( - `${basePublicPath}/bundles/plugin/${id}/`, - `/bundles/plugin/${id}/`, - path, - fileHashCache - ) - ), - buildRouteForBundles( - `${basePublicPath}/bundles/core/`, - `/bundles/core/`, - fromRoot(join('src', 'core', 'target', 'public')), - fileHashCache - ), - buildRouteForBundles( - `${basePublicPath}/bundles/`, - '/bundles/', - regularBundlesPath, - fileHashCache + buildRouteForBundles({ + publicPath: `${basePublicPath}/bundles/plugin/${id}/`, + routePath: `/bundles/plugin/${id}/`, + bundlesPath: path, + fileHashCache, + replacePublicPath: false, + }) ), - buildRouteForBundles( - `${basePublicPath}/built_assets/dlls/`, - '/built_assets/dlls/', - dllBundlesPath, - fileHashCache - ), - buildRouteForBundles(`${basePublicPath}/`, '/built_assets/css/', builtCssPath, fileHashCache), + buildRouteForBundles({ + publicPath: `${basePublicPath}/bundles/core/`, + routePath: `/bundles/core/`, + bundlesPath: fromRoot(join('src', 'core', 'target', 'public')), + fileHashCache, + replacePublicPath: false, + }), + buildRouteForBundles({ + publicPath: `${basePublicPath}/bundles/`, + routePath: '/bundles/', + bundlesPath: regularBundlesPath, + fileHashCache, + }), + buildRouteForBundles({ + publicPath: `${basePublicPath}/built_assets/dlls/`, + routePath: '/built_assets/dlls/', + bundlesPath: dllBundlesPath, + fileHashCache, + }), + buildRouteForBundles({ + publicPath: `${basePublicPath}/`, + routePath: '/built_assets/css/', + bundlesPath: builtCssPath, + fileHashCache, + }), ]; } -function buildRouteForBundles(publicPath, routePath, bundlesPath, fileHashCache) { +function buildRouteForBundles({ + publicPath, + routePath, + bundlesPath, + fileHashCache, + replacePublicPath = true, +}) { return { method: 'GET', path: `${routePath}{path*}`, @@ -129,6 +143,7 @@ function buildRouteForBundles(publicPath, routePath, bundlesPath, fileHashCache) bundlesPath, fileHashCache, publicPath, + replacePublicPath, }); }, }, diff --git a/src/optimize/bundles_route/dynamic_asset_response.js b/src/optimize/bundles_route/dynamic_asset_response.js index 7af780a79e430..80c49a26270fd 100644 --- a/src/optimize/bundles_route/dynamic_asset_response.js +++ b/src/optimize/bundles_route/dynamic_asset_response.js @@ -52,7 +52,7 @@ import { replacePlaceholder } from '../public_path_placeholder'; * @property {LruCache} options.fileHashCache */ export async function createDynamicAssetResponse(options) { - const { request, h, bundlesPath, publicPath, fileHashCache } = options; + const { request, h, bundlesPath, publicPath, fileHashCache, replacePublicPath } = options; let fd; try { @@ -78,11 +78,14 @@ export async function createDynamicAssetResponse(options) { }); fd = null; // read stream is now responsible for fd + const content = replacePublicPath ? replacePlaceholder(read, publicPath) : read; + const etag = replacePublicPath ? `${hash}-${publicPath}` : hash; + return h - .response(replacePlaceholder(read, publicPath)) + .response(content) .takeover() .code(200) - .etag(`${hash}-${publicPath}`) + .etag(etag) .header('cache-control', 'must-revalidate') .type(request.server.mime.path(path).type); } catch (error) { diff --git a/tasks/config/karma.js b/tasks/config/karma.js index 4e106ef3e039a..1ec7c831b4864 100644 --- a/tasks/config/karma.js +++ b/tasks/config/karma.js @@ -53,6 +53,8 @@ module.exports = function(grunt) { function getKarmaFiles(shardNum) { return [ 'http://localhost:5610/test_bundle/built_css.css', + // Sets global variables normally set by the bootstrap.js script + 'http://localhost:5610/test_bundle/karma/globals.js', ...UiSharedDeps.jsDepFilenames.map( chunkFilename => `http://localhost:5610/bundles/kbn-ui-shared-deps/${chunkFilename}` From bc6291349c710ec9332c4fb84d3366ed92824650 Mon Sep 17 00:00:00 2001 From: Nick Partridge Date: Thu, 23 Apr 2020 11:59:19 -0500 Subject: [PATCH 32/39] Update elastic charts v18.4.1 (#64257) --- package.json | 2 +- packages/kbn-ui-shared-deps/package.json | 2 +- .../lens/public/xy_visualization/xy_expression.test.tsx | 2 +- yarn.lock | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index a7729b6dab7a1..9bb308d4cdcf1 100644 --- a/package.json +++ b/package.json @@ -120,7 +120,7 @@ "@babel/core": "^7.9.0", "@babel/register": "^7.9.0", "@elastic/apm-rum": "^5.1.1", - "@elastic/charts": "18.3.0", + "@elastic/charts": "18.4.1", "@elastic/datemath": "5.0.3", "@elastic/ems-client": "7.8.0", "@elastic/eui": "22.3.0", diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json index 3aa01179c00f3..46f55da87575d 100644 --- a/packages/kbn-ui-shared-deps/package.json +++ b/packages/kbn-ui-shared-deps/package.json @@ -9,7 +9,7 @@ "kbn:watch": "node scripts/build --watch" }, "dependencies": { - "@elastic/charts": "18.3.0", + "@elastic/charts": "18.4.1", "@elastic/eui": "22.3.0", "@kbn/i18n": "1.0.0", "abortcontroller-polyfill": "^1.4.0", diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx index 80d33d1b95b61..e75e5fe763d6a 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx @@ -439,7 +439,7 @@ describe('xy_expression', () => { }); test('onElementClick returns correct context data', () => { - const geometry: GeometryValue = { x: 5, y: 1, accessor: 'y1' }; + const geometry: GeometryValue = { x: 5, y: 1, accessor: 'y1', mark: null }; const series = { key: 'spec{d}yAccessor{d}splitAccessors{b-2}', specId: 'd', diff --git a/yarn.lock b/yarn.lock index 30747ee555fe2..79d3911919693 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1236,10 +1236,10 @@ dependencies: "@elastic/apm-rum-core" "^5.2.0" -"@elastic/charts@18.3.0": - version "18.3.0" - resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-18.3.0.tgz#cbdeec1860af274edc7a5f5b9dd26ec48c64bb64" - integrity sha512-4kSlSwdDRsVKVX8vRUkwxOu1IT6WIepgLnP0OZT7cFjgrC1SV/16c3YLw2NZDaVe0M/H4rpeNWW30VyrzZVhyw== +"@elastic/charts@18.4.1": + version "18.4.1" + resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-18.4.1.tgz#19d82c39aef347fd00b33e33b68b683ac4d745b0" + integrity sha512-vV5AAKIKbwgY923OD2Rsr77XFHmsUsWWg/aeCZvG5/b6Yb+fNgM0RF94GADiDMvRvQANhTn2CPPVvNfL18MegQ== dependencies: classnames "^2.2.6" d3-array "^1.2.4" From 09c2727d786ed91a79250c58842c9dbaf53f382d Mon Sep 17 00:00:00 2001 From: Pete Harverson Date: Thu, 23 Apr 2020 18:06:33 +0100 Subject: [PATCH 33/39] [ML] Adds API integration tests for data viz and fields endpoints (#64165) * [ML] Adds API integration tests for data viz and fields endpoints * [ML] Fix review comments and errors from settings endpoints Co-authored-by: Elastic Machine --- .../models/calendar/calendar_manager.ts | 175 ++++---- .../server/models/calendar/event_manager.ts | 36 +- .../ml/server/routes/anomaly_detectors.ts | 4 +- x-pack/plugins/ml/server/routes/apidoc.json | 2 +- .../ml/server/routes/data_visualizer.ts | 12 +- .../ml/server/routes/fields_service.ts | 7 +- .../ml/server/routes/job_audit_messages.ts | 9 +- .../plugins/ml/server/routes/job_service.ts | 7 +- x-pack/plugins/ml/server/routes/modules.ts | 8 +- .../schemas/anomaly_detectors_schema.ts | 2 +- .../routes/schemas/data_visualizer_schema.ts | 15 + .../routes/schemas/fields_service_schema.ts | 9 + .../schemas/job_audit_messages_schema.ts | 5 +- .../routes/schemas/job_service_schema.ts | 1 + .../apis/ml/anomaly_detectors/create.ts | 145 +++++++ .../apis/ml/anomaly_detectors/index.ts | 12 + .../ml/data_visualizer/get_field_stats.ts | 248 ++++++++++++ .../ml/data_visualizer/get_overall_stats.ts | 154 ++++++++ .../apis/ml/data_visualizer/index.ts | 13 + .../ml/fields_service/field_cardinality.ts | 115 ++++++ .../apis/ml/fields_service/index.ts | 13 + .../ml/fields_service/time_field_range.ts | 119 ++++++ x-pack/test/api_integration/apis/ml/index.ts | 12 +- .../bucket_span_estimator.ts | 4 +- .../calculate_model_memory_limit.ts | 4 +- .../apis/ml/job_validation/index.ts | 13 + .../categorization_field_examples.ts | 4 +- .../api_integration/apis/ml/jobs/index.ts | 13 + .../apis/ml/jobs/jobs_summary.ts | 374 ++++++++++++++++++ .../apis/ml/{ => modules}/get_module.ts | 4 +- .../api_integration/apis/ml/modules/index.ts | 14 + .../apis/ml/{ => modules}/recognize_module.ts | 4 +- .../apis/ml/{ => modules}/setup_module.ts | 6 +- 33 files changed, 1404 insertions(+), 159 deletions(-) create mode 100644 x-pack/test/api_integration/apis/ml/anomaly_detectors/create.ts create mode 100644 x-pack/test/api_integration/apis/ml/anomaly_detectors/index.ts create mode 100644 x-pack/test/api_integration/apis/ml/data_visualizer/get_field_stats.ts create mode 100644 x-pack/test/api_integration/apis/ml/data_visualizer/get_overall_stats.ts create mode 100644 x-pack/test/api_integration/apis/ml/data_visualizer/index.ts create mode 100644 x-pack/test/api_integration/apis/ml/fields_service/field_cardinality.ts create mode 100644 x-pack/test/api_integration/apis/ml/fields_service/index.ts create mode 100644 x-pack/test/api_integration/apis/ml/fields_service/time_field_range.ts rename x-pack/test/api_integration/apis/ml/{ => job_validation}/bucket_span_estimator.ts (97%) rename x-pack/test/api_integration/apis/ml/{ => job_validation}/calculate_model_memory_limit.ts (97%) create mode 100644 x-pack/test/api_integration/apis/ml/job_validation/index.ts rename x-pack/test/api_integration/apis/ml/{ => jobs}/categorization_field_examples.ts (98%) create mode 100644 x-pack/test/api_integration/apis/ml/jobs/index.ts create mode 100644 x-pack/test/api_integration/apis/ml/jobs/jobs_summary.ts rename x-pack/test/api_integration/apis/ml/{ => modules}/get_module.ts (92%) create mode 100644 x-pack/test/api_integration/apis/ml/modules/index.ts rename x-pack/test/api_integration/apis/ml/{ => modules}/recognize_module.ts (93%) rename x-pack/test/api_integration/apis/ml/{ => modules}/setup_module.ts (96%) diff --git a/x-pack/plugins/ml/server/models/calendar/calendar_manager.ts b/x-pack/plugins/ml/server/models/calendar/calendar_manager.ts index 64d750f511f3a..581770e59043f 100644 --- a/x-pack/plugins/ml/server/models/calendar/calendar_manager.ts +++ b/x-pack/plugins/ml/server/models/calendar/calendar_manager.ts @@ -5,7 +5,6 @@ */ import { difference } from 'lodash'; -import Boom from 'boom'; import { IScopedClusterClient } from 'kibana/server'; import { EventManager, CalendarEvent } from './event_manager'; @@ -33,43 +32,31 @@ export class CalendarManager { } async getCalendar(calendarId: string) { - try { - const resp = await this._client('ml.calendars', { - calendarId, - }); - - const calendars = resp.calendars; - if (calendars.length) { - const calendar = calendars[0]; - calendar.events = await this._eventManager.getCalendarEvents(calendarId); - return calendar; - } else { - throw Boom.notFound(`Calendar with the id "${calendarId}" not found`); - } - } catch (error) { - throw Boom.badRequest(error); - } + const resp = await this._client('ml.calendars', { + calendarId, + }); + + const calendars = resp.calendars; + const calendar = calendars[0]; // Endpoint throws a 404 if calendar is not found. + calendar.events = await this._eventManager.getCalendarEvents(calendarId); + return calendar; } async getAllCalendars() { - try { - const calendarsResp = await this._client('ml.calendars'); - - const events: CalendarEvent[] = await this._eventManager.getAllEvents(); - const calendars: Calendar[] = calendarsResp.calendars; - calendars.forEach(cal => (cal.events = [])); - - // loop events and combine with related calendars - events.forEach(event => { - const calendar = calendars.find(cal => cal.calendar_id === event.calendar_id); - if (calendar) { - calendar.events.push(event); - } - }); - return calendars; - } catch (error) { - throw Boom.badRequest(error); - } + const calendarsResp = await this._client('ml.calendars'); + + const events: CalendarEvent[] = await this._eventManager.getAllEvents(); + const calendars: Calendar[] = calendarsResp.calendars; + calendars.forEach(cal => (cal.events = [])); + + // loop events and combine with related calendars + events.forEach(event => { + const calendar = calendars.find(cal => cal.calendar_id === event.calendar_id); + if (calendar) { + calendar.events.push(event); + } + }); + return calendars; } /** @@ -78,12 +65,8 @@ export class CalendarManager { * @returns {Promise<*>} */ async getCalendarsByIds(calendarIds: string) { - try { - const calendars: Calendar[] = await this.getAllCalendars(); - return calendars.filter(calendar => calendarIds.includes(calendar.calendar_id)); - } catch (error) { - throw Boom.badRequest(error); - } + const calendars: Calendar[] = await this.getAllCalendars(); + return calendars.filter(calendar => calendarIds.includes(calendar.calendar_id)); } async newCalendar(calendar: FormCalendar) { @@ -91,75 +74,67 @@ export class CalendarManager { const events = calendar.events; delete calendar.calendarId; delete calendar.events; - try { - await this._client('ml.addCalendar', { - calendarId, - body: calendar, - }); - - if (events.length) { - await this._eventManager.addEvents(calendarId, events); - } + await this._client('ml.addCalendar', { + calendarId, + body: calendar, + }); - // return the newly created calendar - return await this.getCalendar(calendarId); - } catch (error) { - throw Boom.badRequest(error); + if (events.length) { + await this._eventManager.addEvents(calendarId, events); } + + // return the newly created calendar + return await this.getCalendar(calendarId); } async updateCalendar(calendarId: string, calendar: Calendar) { const origCalendar: Calendar = await this.getCalendar(calendarId); - try { - // update job_ids - const jobsToAdd = difference(calendar.job_ids, origCalendar.job_ids); - const jobsToRemove = difference(origCalendar.job_ids, calendar.job_ids); - - // workout the differences between the original events list and the new one - // if an event has no event_id, it must be new - const eventsToAdd = calendar.events.filter( - event => origCalendar.events.find(e => this._eventManager.isEqual(e, event)) === undefined - ); - - // if an event in the original calendar cannot be found, it must have been deleted - const eventsToRemove: CalendarEvent[] = origCalendar.events.filter( - event => calendar.events.find(e => this._eventManager.isEqual(e, event)) === undefined - ); - - // note, both of the loops below could be removed if the add and delete endpoints - // allowed multiple job_ids - - // add all new jobs - if (jobsToAdd.length) { - await this._client('ml.addJobToCalendar', { - calendarId, - jobId: jobsToAdd.join(','), - }); - } - - // remove all removed jobs - if (jobsToRemove.length) { - await this._client('ml.removeJobFromCalendar', { - calendarId, - jobId: jobsToRemove.join(','), - }); - } + // update job_ids + const jobsToAdd = difference(calendar.job_ids, origCalendar.job_ids); + const jobsToRemove = difference(origCalendar.job_ids, calendar.job_ids); + + // workout the differences between the original events list and the new one + // if an event has no event_id, it must be new + const eventsToAdd = calendar.events.filter( + event => origCalendar.events.find(e => this._eventManager.isEqual(e, event)) === undefined + ); + + // if an event in the original calendar cannot be found, it must have been deleted + const eventsToRemove: CalendarEvent[] = origCalendar.events.filter( + event => calendar.events.find(e => this._eventManager.isEqual(e, event)) === undefined + ); + + // note, both of the loops below could be removed if the add and delete endpoints + // allowed multiple job_ids + + // add all new jobs + if (jobsToAdd.length) { + await this._client('ml.addJobToCalendar', { + calendarId, + jobId: jobsToAdd.join(','), + }); + } - // add all new events - if (eventsToAdd.length !== 0) { - await this._eventManager.addEvents(calendarId, eventsToAdd); - } + // remove all removed jobs + if (jobsToRemove.length) { + await this._client('ml.removeJobFromCalendar', { + calendarId, + jobId: jobsToRemove.join(','), + }); + } - // remove all removed events - await Promise.all( - eventsToRemove.map(async event => { - await this._eventManager.deleteEvent(calendarId, event.event_id); - }) - ); - } catch (error) { - throw Boom.badRequest(error); + // add all new events + if (eventsToAdd.length !== 0) { + await this._eventManager.addEvents(calendarId, eventsToAdd); } + // remove all removed events + await Promise.all( + eventsToRemove.map(async event => { + await this._eventManager.deleteEvent(calendarId, event.event_id); + }) + ); + // return the updated calendar return await this.getCalendar(calendarId); } diff --git a/x-pack/plugins/ml/server/models/calendar/event_manager.ts b/x-pack/plugins/ml/server/models/calendar/event_manager.ts index 488839f68b3fe..41240e2695f6f 100644 --- a/x-pack/plugins/ml/server/models/calendar/event_manager.ts +++ b/x-pack/plugins/ml/server/models/calendar/event_manager.ts @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import Boom from 'boom'; - import { GLOBAL_CALENDAR } from '../../../common/constants/calendars'; export interface CalendarEvent { @@ -23,41 +21,29 @@ export class EventManager { } async getCalendarEvents(calendarId: string) { - try { - const resp = await this._client('ml.events', { calendarId }); + const resp = await this._client('ml.events', { calendarId }); - return resp.events; - } catch (error) { - throw Boom.badRequest(error); - } + return resp.events; } // jobId is optional async getAllEvents(jobId?: string) { const calendarId = GLOBAL_CALENDAR; - try { - const resp = await this._client('ml.events', { - calendarId, - jobId, - }); + const resp = await this._client('ml.events', { + calendarId, + jobId, + }); - return resp.events; - } catch (error) { - throw Boom.badRequest(error); - } + return resp.events; } async addEvents(calendarId: string, events: CalendarEvent[]) { const body = { events }; - try { - return await this._client('ml.addEvent', { - calendarId, - body, - }); - } catch (error) { - throw Boom.badRequest(error); - } + return await this._client('ml.addEvent', { + calendarId, + body, + }); } async deleteEvent(calendarId: string, eventId: string) { diff --git a/x-pack/plugins/ml/server/routes/anomaly_detectors.ts b/x-pack/plugins/ml/server/routes/anomaly_detectors.ts index a675eb58dc792..ca63d69f403f6 100644 --- a/x-pack/plugins/ml/server/routes/anomaly_detectors.ts +++ b/x-pack/plugins/ml/server/routes/anomaly_detectors.ts @@ -138,12 +138,14 @@ export function jobRoutes({ router, mlLicense }: RouteInitialization) { /** * @apiGroup AnomalyDetectors * - * @api {put} /api/ml/anomaly_detectors/:jobId Instantiate an anomaly detection job + * @api {put} /api/ml/anomaly_detectors/:jobId Create an anomaly detection job * @apiName CreateAnomalyDetectors * @apiDescription Creates an anomaly detection job. * * @apiSchema (params) jobIdSchema * @apiSchema (body) anomalyDetectionJobSchema + * + * @apiSuccess {Object} job the configuration of the job that has been created. */ router.put( { diff --git a/x-pack/plugins/ml/server/routes/apidoc.json b/x-pack/plugins/ml/server/routes/apidoc.json index 4848de6db7049..555053089cb95 100644 --- a/x-pack/plugins/ml/server/routes/apidoc.json +++ b/x-pack/plugins/ml/server/routes/apidoc.json @@ -49,7 +49,7 @@ "GetCategoryExamples", "GetPartitionFieldsValues", - "DataRecognizer", + "Modules", "RecognizeIndex", "GetModule", "SetupModule", diff --git a/x-pack/plugins/ml/server/routes/data_visualizer.ts b/x-pack/plugins/ml/server/routes/data_visualizer.ts index a4c0d5553a4b2..20029fbd8d1a6 100644 --- a/x-pack/plugins/ml/server/routes/data_visualizer.ts +++ b/x-pack/plugins/ml/server/routes/data_visualizer.ts @@ -74,10 +74,12 @@ export function dataVisualizerRoutes({ router, mlLicense }: RouteInitialization) * * @api {post} /api/ml/data_visualizer/get_field_stats/:indexPatternTitle Get stats for fields * @apiName GetStatsForFields - * @apiDescription Returns fields stats of the index pattern. + * @apiDescription Returns the stats on individual fields in the specified index pattern. * * @apiSchema (params) indexPatternTitleSchema * @apiSchema (body) dataVisualizerFieldStatsSchema + * + * @apiSuccess {Object} fieldName stats by field, keyed on the name of the field. */ router.post( { @@ -130,10 +132,16 @@ export function dataVisualizerRoutes({ router, mlLicense }: RouteInitialization) * * @api {post} /api/ml/data_visualizer/get_overall_stats/:indexPatternTitle Get overall stats * @apiName GetOverallStats - * @apiDescription Returns overall stats of the index pattern. + * @apiDescription Returns the top level overall stats for the specified index pattern. * * @apiSchema (params) indexPatternTitleSchema * @apiSchema (body) dataVisualizerOverallStatsSchema + * + * @apiSuccess {number} totalCount total count of documents. + * @apiSuccess {Object} aggregatableExistsFields stats on aggregatable fields that exist in documents. + * @apiSuccess {Object} aggregatableNotExistsFields stats on aggregatable fields that do not exist in documents. + * @apiSuccess {Object} nonAggregatableExistsFields stats on non-aggregatable fields that exist in documents. + * @apiSuccess {Object} nonAggregatableNotExistsFields stats on non-aggregatable fields that do not exist in documents. */ router.post( { diff --git a/x-pack/plugins/ml/server/routes/fields_service.ts b/x-pack/plugins/ml/server/routes/fields_service.ts index 9a5f47409c8a0..577e8e0161342 100644 --- a/x-pack/plugins/ml/server/routes/fields_service.ts +++ b/x-pack/plugins/ml/server/routes/fields_service.ts @@ -37,6 +37,8 @@ export function fieldsService({ router, mlLicense }: RouteInitialization) { * @apiDescription Returns the cardinality of one or more fields. Returns an Object whose keys are the names of the fields, with values equal to the cardinality of the field * * @apiSchema (body) getCardinalityOfFieldsSchema + * + * @apiSuccess {number} fieldName cardinality of the field. */ router.post( { @@ -64,9 +66,12 @@ export function fieldsService({ router, mlLicense }: RouteInitialization) { * * @api {post} /api/ml/fields_service/time_field_range Get time field range * @apiName GetTimeFieldRange - * @apiDescription Returns the timefield range for the given index + * @apiDescription Returns the time range for the given index and query using the specified time range. * * @apiSchema (body) getTimeFieldRangeSchema + * + * @apiSuccess {Object} start start of time range with epoch and string properties. + * @apiSuccess {Object} end end of time range with epoch and string properties. */ router.post( { diff --git a/x-pack/plugins/ml/server/routes/job_audit_messages.ts b/x-pack/plugins/ml/server/routes/job_audit_messages.ts index 71499748691f6..1fe5a7af95d4f 100644 --- a/x-pack/plugins/ml/server/routes/job_audit_messages.ts +++ b/x-pack/plugins/ml/server/routes/job_audit_messages.ts @@ -7,7 +7,10 @@ import { wrapError } from '../client/error_wrapper'; import { RouteInitialization } from '../types'; import { jobAuditMessagesProvider } from '../models/job_audit_messages'; -import { jobAuditMessagesQuerySchema, jobIdSchema } from './schemas/job_audit_messages_schema'; +import { + jobAuditMessagesQuerySchema, + jobAuditMessagesJobIdSchema, +} from './schemas/job_audit_messages_schema'; /** * Routes for job audit message routes @@ -20,14 +23,14 @@ export function jobAuditMessagesRoutes({ router, mlLicense }: RouteInitializatio * @apiName GetJobAuditMessages * @apiDescription Returns audit messages for specified job ID * - * @apiSchema (params) jobIdSchema + * @apiSchema (params) jobAuditMessagesJobIdSchema * @apiSchema (query) jobAuditMessagesQuerySchema */ router.get( { path: '/api/ml/job_audit_messages/messages/{jobId}', validate: { - params: jobIdSchema, + params: jobAuditMessagesJobIdSchema, query: jobAuditMessagesQuerySchema, }, }, diff --git a/x-pack/plugins/ml/server/routes/job_service.ts b/x-pack/plugins/ml/server/routes/job_service.ts index 493974cbafe36..cf973a914391c 100644 --- a/x-pack/plugins/ml/server/routes/job_service.ts +++ b/x-pack/plugins/ml/server/routes/job_service.ts @@ -176,9 +176,14 @@ export function jobServiceRoutes({ router, mlLicense }: RouteInitialization) { * * @api {post} /api/ml/jobs/jobs_summary Jobs summary * @apiName JobsSummary - * @apiDescription Creates a summary jobs list. Jobs include job stats, datafeed stats, and calendars. + * @apiDescription Returns a list of anomaly detection jobs, with summary level information for every job. + * For any supplied job IDs, full job information will be returned, which include the analysis configuration, + * job stats, datafeed stats, and calendars. * * @apiSchema (body) jobIdsSchema + * + * @apiSuccess {Array} jobsList list of jobs. For any supplied job IDs, the job object will contain a fullJob property + * which includes the full configuration and stats for the job. */ router.post( { diff --git a/x-pack/plugins/ml/server/routes/modules.ts b/x-pack/plugins/ml/server/routes/modules.ts index 2d462b6dc207a..2891144fc4574 100644 --- a/x-pack/plugins/ml/server/routes/modules.ts +++ b/x-pack/plugins/ml/server/routes/modules.ts @@ -81,7 +81,7 @@ function dataRecognizerJobsExist(context: RequestHandlerContext, moduleId: strin */ export function dataRecognizer({ router, mlLicense }: RouteInitialization) { /** - * @apiGroup DataRecognizer + * @apiGroup Modules * * @api {get} /api/ml/modules/recognize/:indexPatternTitle Recognize index pattern * @apiName RecognizeIndex @@ -111,7 +111,7 @@ export function dataRecognizer({ router, mlLicense }: RouteInitialization) { ); /** - * @apiGroup DataRecognizer + * @apiGroup Modules * * @api {get} /api/ml/modules/get_module/:moduleId Get module * @apiName GetModule @@ -146,7 +146,7 @@ export function dataRecognizer({ router, mlLicense }: RouteInitialization) { ); /** - * @apiGroup DataRecognizer + * @apiGroup Modules * * @api {post} /api/ml/modules/setup/:moduleId Setup module * @apiName SetupModule @@ -204,7 +204,7 @@ export function dataRecognizer({ router, mlLicense }: RouteInitialization) { ); /** - * @apiGroup DataRecognizer + * @apiGroup Modules * * @api {post} /api/ml/modules/jobs_exist/:moduleId Check if module jobs exist * @apiName CheckExistingModuleJobs diff --git a/x-pack/plugins/ml/server/routes/schemas/anomaly_detectors_schema.ts b/x-pack/plugins/ml/server/routes/schemas/anomaly_detectors_schema.ts index ab1305d9bc354..9b86e3e06096e 100644 --- a/x-pack/plugins/ml/server/routes/schemas/anomaly_detectors_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/anomaly_detectors_schema.ts @@ -119,7 +119,7 @@ export const anomalyDetectionJobSchema = { }; export const jobIdSchema = schema.object({ - /** Job id */ + /** Job ID. */ jobId: schema.string(), }); diff --git a/x-pack/plugins/ml/server/routes/schemas/data_visualizer_schema.ts b/x-pack/plugins/ml/server/routes/schemas/data_visualizer_schema.ts index 1a1d02f991b55..b2d665954bd4d 100644 --- a/x-pack/plugins/ml/server/routes/schemas/data_visualizer_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/data_visualizer_schema.ts @@ -7,26 +7,41 @@ import { schema } from '@kbn/config-schema'; export const indexPatternTitleSchema = schema.object({ + /** Title of the index pattern for which to return stats. */ indexPatternTitle: schema.string(), }); export const dataVisualizerFieldStatsSchema = schema.object({ + /** Query to match documents in the index. */ query: schema.any(), fields: schema.arrayOf(schema.any()), + /** Number of documents to be collected in the sample processed on each shard, or -1 for no sampling. */ samplerShardSize: schema.number(), + /** Name of the time field in the index (optional). */ timeFieldName: schema.maybe(schema.string()), + /** Earliest timestamp for search, as epoch ms (optional). */ earliest: schema.maybe(schema.number()), + /** Latest timestamp for search, as epoch ms (optional). */ latest: schema.maybe(schema.number()), + /** Aggregation interval to use for obtaining document counts over time (optional). */ interval: schema.maybe(schema.string()), + /** Maximum number of examples to return for text type fields. */ maxExamples: schema.number(), }); export const dataVisualizerOverallStatsSchema = schema.object({ + /** Query to match documents in the index. */ query: schema.any(), + /** Names of aggregatable fields for which to return stats. */ aggregatableFields: schema.arrayOf(schema.string()), + /** Names of non-aggregatable fields for which to return stats. */ nonAggregatableFields: schema.arrayOf(schema.string()), + /** Number of documents to be collected in the sample processed on each shard, or -1 for no sampling. */ samplerShardSize: schema.number(), + /** Name of the time field in the index (optional). */ timeFieldName: schema.maybe(schema.string()), + /** Earliest timestamp for search, as epoch ms (optional). */ earliest: schema.maybe(schema.number()), + /** Latest timestamp for search, as epoch ms (optional). */ latest: schema.maybe(schema.number()), }); diff --git a/x-pack/plugins/ml/server/routes/schemas/fields_service_schema.ts b/x-pack/plugins/ml/server/routes/schemas/fields_service_schema.ts index e0fba498e0d58..ba397e0084e27 100644 --- a/x-pack/plugins/ml/server/routes/schemas/fields_service_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/fields_service_schema.ts @@ -7,16 +7,25 @@ import { schema } from '@kbn/config-schema'; export const getCardinalityOfFieldsSchema = schema.object({ + /** Index or indexes for which to return the time range. */ index: schema.oneOf([schema.string(), schema.arrayOf(schema.string())]), + /** Name(s) of the field(s) to return cardinality information. */ fieldNames: schema.maybe(schema.arrayOf(schema.string())), + /** Query to match documents in the index(es) (optional). */ query: schema.maybe(schema.any()), + /** Name of the time field in the index. */ timeFieldName: schema.maybe(schema.string()), + /** Earliest timestamp for search, as epoch ms (optional). */ earliestMs: schema.maybe(schema.oneOf([schema.number(), schema.string()])), + /** Latest timestamp for search, as epoch ms (optional). */ latestMs: schema.maybe(schema.oneOf([schema.number(), schema.string()])), }); export const getTimeFieldRangeSchema = schema.object({ + /** Index or indexes for which to return the time range. */ index: schema.oneOf([schema.string(), schema.arrayOf(schema.string())]), + /** Name of the time field in the index. */ timeFieldName: schema.maybe(schema.string()), + /** Query to match documents in the index(es). */ query: schema.maybe(schema.any()), }); diff --git a/x-pack/plugins/ml/server/routes/schemas/job_audit_messages_schema.ts b/x-pack/plugins/ml/server/routes/schemas/job_audit_messages_schema.ts index b94a004384eb1..ac489b3a6ce6f 100644 --- a/x-pack/plugins/ml/server/routes/schemas/job_audit_messages_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/job_audit_messages_schema.ts @@ -6,7 +6,10 @@ import { schema } from '@kbn/config-schema'; -export const jobIdSchema = schema.object({ jobId: schema.maybe(schema.string()) }); +export const jobAuditMessagesJobIdSchema = schema.object({ + /** Job ID. */ + jobId: schema.maybe(schema.string()), +}); export const jobAuditMessagesQuerySchema = schema.maybe( schema.object({ from: schema.maybe(schema.any()) }) diff --git a/x-pack/plugins/ml/server/routes/schemas/job_service_schema.ts b/x-pack/plugins/ml/server/routes/schemas/job_service_schema.ts index d2036b8a7c0fa..1ca1e5287e9d0 100644 --- a/x-pack/plugins/ml/server/routes/schemas/job_service_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/job_service_schema.ts @@ -40,6 +40,7 @@ export const forceStartDatafeedSchema = schema.object({ }); export const jobIdsSchema = schema.object({ + /** Optional list of job ID(s). */ jobIds: schema.maybe( schema.oneOf([schema.string(), schema.arrayOf(schema.maybe(schema.string()))]) ), diff --git a/x-pack/test/api_integration/apis/ml/anomaly_detectors/create.ts b/x-pack/test/api_integration/apis/ml/anomaly_detectors/create.ts new file mode 100644 index 0000000000000..bbc766df34dcf --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/anomaly_detectors/create.ts @@ -0,0 +1,145 @@ +/* + * 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 { USER } from '../../../../functional/services/machine_learning/security_common'; + +const COMMON_HEADERS = { + 'kbn-xsrf': 'some-xsrf-token', +}; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertestWithoutAuth'); + const ml = getService('ml'); + + const jobId = `fq_single_${Date.now()}`; + + const testDataList = [ + { + testTitle: 'ML Poweruser creates a single metric job', + user: USER.ML_POWERUSER, + jobId: `${jobId}_1`, + requestBody: { + job_id: `${jobId}_1`, + description: + 'Single metric job based on the farequote dataset with 30m bucketspan and mean(responsetime)', + groups: ['automated', 'farequote', 'single-metric'], + analysis_config: { + bucket_span: '30m', + detectors: [{ function: 'mean', field_name: 'responsetime' }], + influencers: [], + summary_count_field_name: 'doc_count', + }, + data_description: { time_field: '@timestamp' }, + analysis_limits: { model_memory_limit: '11MB' }, + model_plot_config: { enabled: true }, + }, + expected: { + responseCode: 200, + responseBody: { + job_id: `${jobId}_1`, + job_type: 'anomaly_detector', + groups: ['automated', 'farequote', 'single-metric'], + description: + 'Single metric job based on the farequote dataset with 30m bucketspan and mean(responsetime)', + analysis_config: { + bucket_span: '30m', + summary_count_field_name: 'doc_count', + detectors: [ + { + detector_description: 'mean(responsetime)', + function: 'mean', + field_name: 'responsetime', + detector_index: 0, + }, + ], + influencers: [], + }, + analysis_limits: { model_memory_limit: '11mb', categorization_examples_limit: 4 }, + data_description: { time_field: '@timestamp', time_format: 'epoch_ms' }, + model_plot_config: { enabled: true }, + model_snapshot_retention_days: 1, + results_index_name: 'shared', + allow_lazy_open: false, + }, + }, + }, + { + testTitle: 'ML viewer cannot create a job', + user: USER.ML_VIEWER, + jobId: `${jobId}_2`, + requestBody: { + job_id: `${jobId}_2`, + description: + 'Single metric job based on the farequote dataset with 30m bucketspan and mean(responsetime)', + groups: ['automated', 'farequote', 'single-metric'], + analysis_config: { + bucket_span: '30m', + detectors: [{ function: 'mean', field_name: 'responsetime' }], + influencers: [], + summary_count_field_name: 'doc_count', + }, + data_description: { time_field: '@timestamp' }, + analysis_limits: { model_memory_limit: '11MB' }, + model_plot_config: { enabled: true }, + }, + expected: { + responseCode: 403, + responseBody: { + statusCode: 403, + error: 'Forbidden', + message: + '[security_exception] action [cluster:admin/xpack/ml/job/put] is unauthorized for user [ml_viewer]', + }, + }, + }, + ]; + + describe('create', function() { + before(async () => { + await esArchiver.loadIfNeeded('ml/farequote'); + await ml.testResources.setKibanaTimeZoneToUTC(); + }); + + after(async () => { + await ml.api.cleanMlIndices(); + }); + + for (const testData of testDataList) { + it(`${testData.testTitle}`, async () => { + const { body } = await supertest + .put(`/api/ml/anomaly_detectors/${testData.jobId}`) + .auth(testData.user, ml.securityCommon.getPasswordForUser(testData.user)) + .set(COMMON_HEADERS) + .send(testData.requestBody) + .expect(testData.expected.responseCode); + + if (body.error === undefined) { + // Validate the important parts of the response. + const expectedResponse = testData.expected.responseBody; + expect(body.job_id).to.eql(expectedResponse.job_id); + expect(body.groups).to.eql(expectedResponse.groups); + expect(body.analysis_config!.bucket_span).to.eql( + expectedResponse.analysis_config!.bucket_span + ); + expect(body.analysis_config.detectors).to.have.length( + expectedResponse.analysis_config!.detectors.length + ); + expect(body.analysis_config.detectors[0]).to.eql( + expectedResponse.analysis_config!.detectors[0] + ); + } else { + expect(body.error).to.eql(testData.expected.responseBody.error); + expect(body.message).to.eql(testData.expected.responseBody.message); + } + }); + } + }); +}; diff --git a/x-pack/test/api_integration/apis/ml/anomaly_detectors/index.ts b/x-pack/test/api_integration/apis/ml/anomaly_detectors/index.ts new file mode 100644 index 0000000000000..fb8acaf5c3ae9 --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/anomaly_detectors/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('anomaly detectors', function() { + loadTestFile(require.resolve('./create')); + }); +} diff --git a/x-pack/test/api_integration/apis/ml/data_visualizer/get_field_stats.ts b/x-pack/test/api_integration/apis/ml/data_visualizer/get_field_stats.ts new file mode 100644 index 0000000000000..dfa81b5d78c65 --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/data_visualizer/get_field_stats.ts @@ -0,0 +1,248 @@ +/* + * 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 { USER } from '../../../../functional/services/machine_learning/security_common'; + +const COMMON_HEADERS = { + 'kbn-xsrf': 'some-xsrf-token', +}; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertestWithoutAuth'); + const ml = getService('ml'); + + const metricFieldsTestData = { + testTitle: 'returns stats for metric fields over all time', + index: 'ft_farequote', + user: USER.ML_POWERUSER, + requestBody: { + query: { + bool: { + must: { + term: { airline: 'JZA' }, // Only use one airline to ensure no sampling. + }, + }, + }, + fields: [ + { type: 'number', cardinality: 0 }, + { fieldName: 'responsetime', type: 'number', cardinality: 4249 }, + ], + samplerShardSize: -1, // No sampling, as otherwise counts could vary on each run. + timeFieldName: '@timestamp', + interval: '1d', + maxExamples: 10, + }, + expected: { + responseCode: 200, + responseBody: [ + { + documentCounts: { + interval: '1d', + buckets: { + '1454803200000': 846, + '1454889600000': 846, + '1454976000000': 859, + '1455062400000': 851, + '1455148800000': 858, + }, + }, + }, + { + // Cannot verify median and percentiles responses as the ES percentiles agg is non-deterministic. + fieldName: 'responsetime', + count: 4260, + min: 963.4293212890625, + max: 1042.13525390625, + avg: 1000.0378077547315, + isTopValuesSampled: false, + topValues: [ + { key: 980.0411987304688, doc_count: 2 }, + { key: 989.278076171875, doc_count: 2 }, + { key: 989.763916015625, doc_count: 2 }, + { key: 991.290771484375, doc_count: 2 }, + { key: 992.0765991210938, doc_count: 2 }, + { key: 993.8115844726562, doc_count: 2 }, + { key: 993.8973999023438, doc_count: 2 }, + { key: 994.0230102539062, doc_count: 2 }, + { key: 994.364990234375, doc_count: 2 }, + { key: 994.916015625, doc_count: 2 }, + ], + topValuesSampleSize: 4260, + topValuesSamplerShardSize: -1, + }, + ], + }, + }; + + const nonMetricFieldsTestData = { + testTitle: 'returns stats for non-metric fields specifying query and time range', + index: 'ft_farequote', + user: USER.ML_POWERUSER, + requestBody: { + query: { + bool: { + must: { + term: { airline: 'AAL' }, + }, + }, + }, + fields: [ + { fieldName: '@timestamp', type: 'date', cardinality: 4751 }, + { fieldName: '@version.keyword', type: 'keyword', cardinality: 1 }, + { fieldName: 'airline', type: 'keyword', cardinality: 19 }, + { fieldName: 'type', type: 'text', cardinality: 0 }, + { fieldName: 'type.keyword', type: 'keyword', cardinality: 1 }, + ], + samplerShardSize: -1, // No sampling, as otherwise counts would vary on each run. + timeFieldName: '@timestamp', + earliest: 1454889600000, // February 8, 2016 12:00:00 AM GMT + latest: 1454976000000, // February 9, 2016 12:00:00 AM GMT + maxExamples: 10, + }, + expected: { + responseCode: 200, + responseBody: [ + { fieldName: '@timestamp', count: 1733, earliest: 1454889602000, latest: 1454975948000 }, + { + fieldName: '@version.keyword', + isTopValuesSampled: false, + topValues: [{ key: '1', doc_count: 1733 }], + topValuesSampleSize: 1733, + topValuesSamplerShardSize: -1, + }, + { + fieldName: 'airline', + isTopValuesSampled: false, + topValues: [{ key: 'AAL', doc_count: 1733 }], + topValuesSampleSize: 1733, + topValuesSamplerShardSize: -1, + }, + { + fieldName: 'type.keyword', + isTopValuesSampled: false, + topValues: [{ key: 'farequote', doc_count: 1733 }], + topValuesSampleSize: 1733, + topValuesSamplerShardSize: -1, + }, + { fieldName: 'type', examples: ['farequote'] }, + ], + }, + }; + + const errorTestData = { + testTitle: 'returns error for index which does not exist', + index: 'ft_farequote_not_exists', + user: USER.ML_POWERUSER, + requestBody: { + query: { bool: { must: [{ match_all: {} }] } }, + fields: [ + { type: 'number', cardinality: 0 }, + { fieldName: 'responsetime', type: 'number', cardinality: 4249 }, + ], + samplerShardSize: -1, // No sampling, as otherwise counts could vary on each run. + timeFieldName: '@timestamp', + maxExamples: 10, + }, + expected: { + responseCode: 404, + responseBody: { + statusCode: 404, + error: 'Not Found', + message: + '[index_not_found_exception] no such index [ft_farequote_not_exists], with { resource.type="index_or_alias" & resource.id="ft_farequote_not_exists" & index_uuid="_na_" & index="ft_farequote_not_exists" }', + }, + }, + }; + + async function runGetFieldStatsRequest( + index: string, + user: USER, + requestBody: object, + expectedResponsecode: number + ): Promise { + const { body } = await supertest + .post(`/api/ml/data_visualizer/get_field_stats/${index}`) + .auth(user, ml.securityCommon.getPasswordForUser(user)) + .set(COMMON_HEADERS) + .send(requestBody) + .expect(expectedResponsecode); + + return body; + } + + function compareByFieldName(a: { fieldName: string }, b: { fieldName: string }) { + if (a.fieldName < b.fieldName) { + return -1; + } + if (a.fieldName > b.fieldName) { + return 1; + } + return 0; + } + + describe('get_field_stats', function() { + before(async () => { + await esArchiver.loadIfNeeded('ml/farequote'); + await ml.testResources.setKibanaTimeZoneToUTC(); + }); + + it(`${metricFieldsTestData.testTitle}`, async () => { + const body = await runGetFieldStatsRequest( + metricFieldsTestData.index, + metricFieldsTestData.user, + metricFieldsTestData.requestBody, + metricFieldsTestData.expected.responseCode + ); + + // Cannot verify median and percentiles responses as the ES percentiles agg is non-deterministic. + const expected = metricFieldsTestData.expected; + expect(body).to.have.length(expected.responseBody.length); + + const actualDocCounts = body[0]; + const expectedDocCounts = expected.responseBody[0]; + expect(actualDocCounts).to.eql(expectedDocCounts); + + const actualFieldData = { ...body[1] }; + delete actualFieldData.median; + delete actualFieldData.distribution; + + expect(actualFieldData).to.eql(expected.responseBody[1]); + }); + + it(`${nonMetricFieldsTestData.testTitle}`, async () => { + const body = await runGetFieldStatsRequest( + nonMetricFieldsTestData.index, + nonMetricFieldsTestData.user, + nonMetricFieldsTestData.requestBody, + nonMetricFieldsTestData.expected.responseCode + ); + + // Sort the fields in the response before validating. + const expectedRspFields = nonMetricFieldsTestData.expected.responseBody.sort( + compareByFieldName + ); + const actualRspFields = body.sort(compareByFieldName); + expect(actualRspFields).to.eql(expectedRspFields); + }); + + it(`${errorTestData.testTitle}`, async () => { + const body = await runGetFieldStatsRequest( + errorTestData.index, + errorTestData.user, + errorTestData.requestBody, + errorTestData.expected.responseCode + ); + + expect(body.error).to.eql(errorTestData.expected.responseBody.error); + expect(body.message).to.eql(errorTestData.expected.responseBody.message); + }); + }); +}; diff --git a/x-pack/test/api_integration/apis/ml/data_visualizer/get_overall_stats.ts b/x-pack/test/api_integration/apis/ml/data_visualizer/get_overall_stats.ts new file mode 100644 index 0000000000000..6490c19c64483 --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/data_visualizer/get_overall_stats.ts @@ -0,0 +1,154 @@ +/* + * 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 { USER } from '../../../../functional/services/machine_learning/security_common'; + +const COMMON_HEADERS = { + 'kbn-xsrf': 'some-xsrf-token', +}; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertestWithoutAuth'); + const ml = getService('ml'); + + const testDataList = [ + { + testTitle: 'returns stats over all time', + index: 'ft_farequote', + user: USER.ML_POWERUSER, + requestBody: { + query: { bool: { must: [{ match_all: {} }] } }, + aggregatableFields: ['@timestamp', 'airline', 'responsetime', 'sourcetype'], + nonAggregatableFields: ['type'], + samplerShardSize: -1, // No sampling, as otherwise counts would vary on each run. + timeFieldName: '@timestamp', + }, + expected: { + responseCode: 200, + responseBody: { + totalCount: 86274, + aggregatableExistsFields: [ + { + fieldName: '@timestamp', + existsInDocs: true, + stats: { sampleCount: 86274, count: 86274, cardinality: 78580 }, + }, + { + fieldName: 'airline', + existsInDocs: true, + stats: { sampleCount: 86274, count: 86274, cardinality: 19 }, + }, + { + fieldName: 'responsetime', + existsInDocs: true, + stats: { sampleCount: 86274, count: 86274, cardinality: 83346 }, + }, + ], + aggregatableNotExistsFields: [{ fieldName: 'sourcetype', existsInDocs: false }], + nonAggregatableExistsFields: [{ fieldName: 'type', existsInDocs: true, stats: {} }], + nonAggregatableNotExistsFields: [], + }, + }, + }, + { + testTitle: 'returns stats when specifying query and time range', + index: 'ft_farequote', + user: USER.ML_POWERUSER, + requestBody: { + query: { + bool: { + must: { + term: { airline: 'AAL' }, + }, + }, + }, + aggregatableFields: ['@timestamp', 'airline', 'responsetime', 'sourcetype'], + nonAggregatableFields: ['type'], + samplerShardSize: -1, // No sampling, as otherwise counts would vary on each run. + timeFieldName: '@timestamp', + earliest: 1454889600000, // February 8, 2016 12:00:00 AM GMT + latest: 1454976000000, // February 9, 2016 12:00:00 AM GMT + }, + expected: { + responseCode: 200, + responseBody: { + totalCount: 1733, + aggregatableExistsFields: [ + { + fieldName: '@timestamp', + existsInDocs: true, + stats: { sampleCount: 1733, count: 1733, cardinality: 1713 }, + }, + { + fieldName: 'airline', + existsInDocs: true, + stats: { sampleCount: 1733, count: 1733, cardinality: 1 }, + }, + { + fieldName: 'responsetime', + existsInDocs: true, + stats: { sampleCount: 1733, count: 1733, cardinality: 1730 }, + }, + ], + aggregatableNotExistsFields: [{ fieldName: 'sourcetype', existsInDocs: false }], + nonAggregatableExistsFields: [{ fieldName: 'type', existsInDocs: true, stats: {} }], + nonAggregatableNotExistsFields: [], + }, + }, + }, + { + testTitle: 'returns error for index which does not exist', + index: 'ft_farequote_not_exist', + user: USER.ML_POWERUSER, + requestBody: { + query: { bool: { must: [{ match_all: {} }] } }, + aggregatableFields: ['@timestamp', 'airline', 'responsetime', 'sourcetype'], + nonAggregatableFields: ['@version', 'type'], + samplerShardSize: 1000, + timeFieldName: '@timestamp', + }, + expected: { + responseCode: 404, + responseBody: { + statusCode: 404, + error: 'Not Found', + message: + '[index_not_found_exception] no such index [ft_farequote_not_exist], with { resource.type="index_or_alias" & resource.id="ft_farequote_not_exist" & index_uuid="_na_" & index="ft_farequote_not_exist" }', + }, + }, + }, + ]; + + describe('get_overall_stats', function() { + before(async () => { + await esArchiver.loadIfNeeded('ml/farequote'); + await ml.testResources.setKibanaTimeZoneToUTC(); + }); + + for (const testData of testDataList) { + it(`${testData.testTitle}`, async () => { + const { body } = await supertest + .post(`/api/ml/data_visualizer/get_overall_stats/${testData.index}`) + .auth(testData.user, ml.securityCommon.getPasswordForUser(testData.user)) + .set(COMMON_HEADERS) + .send(testData.requestBody) + .expect(testData.expected.responseCode); + + if (body.error === undefined) { + expect(body).to.eql(testData.expected.responseBody); + } else { + expect(body.error).to.eql(testData.expected.responseBody.error); + expect(body.message).to.eql(testData.expected.responseBody.message); + } + }); + } + }); +}; diff --git a/x-pack/test/api_integration/apis/ml/data_visualizer/index.ts b/x-pack/test/api_integration/apis/ml/data_visualizer/index.ts new file mode 100644 index 0000000000000..ce9e44618f1af --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/data_visualizer/index.ts @@ -0,0 +1,13 @@ +/* + * 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('data visualizer', function() { + loadTestFile(require.resolve('./get_field_stats')); + loadTestFile(require.resolve('./get_overall_stats')); + }); +} diff --git a/x-pack/test/api_integration/apis/ml/fields_service/field_cardinality.ts b/x-pack/test/api_integration/apis/ml/fields_service/field_cardinality.ts new file mode 100644 index 0000000000000..245375562b5c1 --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/fields_service/field_cardinality.ts @@ -0,0 +1,115 @@ +/* + * 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 { USER } from '../../../../functional/services/machine_learning/security_common'; + +const COMMON_HEADERS = { + 'kbn-xsrf': 'some-xsrf-token', +}; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertestWithoutAuth'); + const ml = getService('ml'); + + const testDataList = [ + { + testTitle: 'returns cardinality of customer name fields over full time range', + user: USER.ML_POWERUSER, + requestBody: { + index: 'ft_ecommerce', + fieldNames: ['customer_first_name.keyword', 'customer_last_name.keyword'], + query: { bool: { must: [{ match_all: {} }] } }, + timeFieldName: 'order_date', + }, + expected: { + responseBody: { + 'customer_first_name.keyword': 46, + 'customer_last_name.keyword': 183, + }, + }, + }, + { + testTitle: 'returns cardinality of geoip fields over specified range', + user: USER.ML_POWERUSER, + requestBody: { + index: 'ft_ecommerce', + fieldNames: ['geoip.city_name', 'geoip.continent_name', 'geoip.country_iso_code'], + query: { bool: { must: [{ match_all: {} }] } }, + timeFieldName: 'order_date', + earliestMs: 1560556800000, // June 15, 2019 12:00:00 AM GMT + latestMs: 1560643199000, // June 15, 2019 11:59:59 PM GMT + }, + expected: { + responseBody: { + 'geoip.city_name': 10, + 'geoip.continent_name': 5, + 'geoip.country_iso_code': 9, + }, + }, + }, + { + testTitle: 'returns empty response for non aggregatable field', + user: USER.ML_POWERUSER, + requestBody: { + index: 'ft_ecommerce', + fieldNames: ['manufacturer'], + query: { bool: { must: [{ match_all: {} }] } }, + timeFieldName: 'order_date', + earliestMs: 1560556800000, // June 15, 2019 12:00:00 AM GMT + latestMs: 1560643199000, // June 15, 2019 11:59:59 PM GMT + }, + expected: { + responseBody: {}, + }, + }, + { + testTitle: 'returns error for index which does not exist', + user: USER.ML_POWERUSER, + requestBody: { + index: 'ft_ecommerce_not_exist', + fieldNames: ['customer_first_name.keyword', 'customer_last_name.keyword'], + timeFieldName: 'order_date', + }, + expected: { + responseBody: { + statusCode: 404, + error: 'Not Found', + message: + '[index_not_found_exception] no such index [ft_ecommerce_not_exist], with { resource.type="index_or_alias" & resource.id="ft_ecommerce_not_exist" & index_uuid="_na_" & index="ft_ecommerce_not_exist" }', + }, + }, + }, + ]; + + describe('field_cardinality', function() { + before(async () => { + await esArchiver.loadIfNeeded('ml/ecommerce'); + await ml.testResources.setKibanaTimeZoneToUTC(); + }); + + for (const testData of testDataList) { + it(`${testData.testTitle}`, async () => { + const { body } = await supertest + .post('/api/ml/fields_service/field_cardinality') + .auth(testData.user, ml.securityCommon.getPasswordForUser(testData.user)) + .set(COMMON_HEADERS) + .send(testData.requestBody); + + if (body.error === undefined) { + expect(body).to.eql(testData.expected.responseBody); + } else { + expect(body.error).to.eql(testData.expected.responseBody.error); + expect(body.message).to.eql(testData.expected.responseBody.message); + } + }); + } + }); +}; diff --git a/x-pack/test/api_integration/apis/ml/fields_service/index.ts b/x-pack/test/api_integration/apis/ml/fields_service/index.ts new file mode 100644 index 0000000000000..312602e589119 --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/fields_service/index.ts @@ -0,0 +1,13 @@ +/* + * 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('fields service', function() { + loadTestFile(require.resolve('./field_cardinality')); + loadTestFile(require.resolve('./time_field_range')); + }); +} diff --git a/x-pack/test/api_integration/apis/ml/fields_service/time_field_range.ts b/x-pack/test/api_integration/apis/ml/fields_service/time_field_range.ts new file mode 100644 index 0000000000000..2f0fd4fc6c5e3 --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/fields_service/time_field_range.ts @@ -0,0 +1,119 @@ +/* + * 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 { USER } from '../../../../functional/services/machine_learning/security_common'; + +const COMMON_HEADERS = { + 'kbn-xsrf': 'some-xsrf-token', +}; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertestWithoutAuth'); + const ml = getService('ml'); + + const testDataList = [ + { + testTitle: 'returns expected time range with index and match_all query', + user: USER.ML_POWERUSER, + requestBody: { + index: 'ft_ecommerce', + query: { bool: { must: [{ match_all: {} }] } }, + timeFieldName: 'order_date', + }, + expected: { + responseCode: 200, + responseBody: { + start: { + epoch: 1560297859000, + string: '2019-06-12T00:04:19.000Z', + }, + end: { + epoch: 1562975136000, + string: '2019-07-12T23:45:36.000Z', + }, + success: true, + }, + }, + }, + { + testTitle: 'returns expected time range with index and query', + user: USER.ML_POWERUSER, + requestBody: { + index: 'ft_ecommerce', + query: { + term: { + 'customer_first_name.keyword': { + value: 'Brigitte', + }, + }, + }, + timeFieldName: 'order_date', + }, + expected: { + responseCode: 200, + responseBody: { + start: { + epoch: 1560298982000, + string: '2019-06-12T00:23:02.000Z', + }, + end: { + epoch: 1562973754000, + string: '2019-07-12T23:22:34.000Z', + }, + success: true, + }, + }, + }, + { + testTitle: 'returns error for index which does not exist', + user: USER.ML_POWERUSER, + requestBody: { + index: 'ft_ecommerce_not_exist', + query: { bool: { must: [{ match_all: {} }] } }, + timeFieldName: 'order_date', + }, + expected: { + responseCode: 404, + responseBody: { + statusCode: 404, + error: 'Not Found', + message: + '[index_not_found_exception] no such index [ft_ecommerce_not_exist], with { resource.type="index_or_alias" & resource.id="ft_ecommerce_not_exist" & index_uuid="_na_" & index="ft_ecommerce_not_exist" }', + }, + }, + }, + ]; + + describe('time_field_range', function() { + before(async () => { + await esArchiver.loadIfNeeded('ml/ecommerce'); + await ml.testResources.setKibanaTimeZoneToUTC(); + }); + + for (const testData of testDataList) { + it(`${testData.testTitle}`, async () => { + const { body } = await supertest + .post('/api/ml/fields_service/time_field_range') + .auth(testData.user, ml.securityCommon.getPasswordForUser(testData.user)) + .set(COMMON_HEADERS) + .send(testData.requestBody) + .expect(testData.expected.responseCode); + + if (body.error === undefined) { + expect(body).to.eql(testData.expected.responseBody); + } else { + expect(body.error).to.eql(testData.expected.responseBody.error); + expect(body.message).to.eql(testData.expected.responseBody.message); + } + }); + } + }); +}; diff --git a/x-pack/test/api_integration/apis/ml/index.ts b/x-pack/test/api_integration/apis/ml/index.ts index f012883c46ca3..58356637c63ac 100644 --- a/x-pack/test/api_integration/apis/ml/index.ts +++ b/x-pack/test/api_integration/apis/ml/index.ts @@ -31,11 +31,11 @@ export default function({ getService, loadTestFile }: FtrProviderContext) { await ml.testResources.resetKibanaTimeZone(); }); - loadTestFile(require.resolve('./bucket_span_estimator')); - loadTestFile(require.resolve('./calculate_model_memory_limit')); - loadTestFile(require.resolve('./categorization_field_examples')); - loadTestFile(require.resolve('./get_module')); - loadTestFile(require.resolve('./recognize_module')); - loadTestFile(require.resolve('./setup_module')); + loadTestFile(require.resolve('./modules')); + loadTestFile(require.resolve('./anomaly_detectors')); + loadTestFile(require.resolve('./data_visualizer')); + loadTestFile(require.resolve('./fields_service')); + loadTestFile(require.resolve('./job_validation')); + loadTestFile(require.resolve('./jobs')); }); } diff --git a/x-pack/test/api_integration/apis/ml/bucket_span_estimator.ts b/x-pack/test/api_integration/apis/ml/job_validation/bucket_span_estimator.ts similarity index 97% rename from x-pack/test/api_integration/apis/ml/bucket_span_estimator.ts rename to x-pack/test/api_integration/apis/ml/job_validation/bucket_span_estimator.ts index bc0dc3019d7c9..0b4aca9660be4 100644 --- a/x-pack/test/api_integration/apis/ml/bucket_span_estimator.ts +++ b/x-pack/test/api_integration/apis/ml/job_validation/bucket_span_estimator.ts @@ -6,8 +6,8 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; -import { USER } from '../../../functional/services/machine_learning/security_common'; +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { USER } from '../../../../functional/services/machine_learning/security_common'; const COMMON_HEADERS = { 'kbn-xsrf': 'some-xsrf-token', diff --git a/x-pack/test/api_integration/apis/ml/calculate_model_memory_limit.ts b/x-pack/test/api_integration/apis/ml/job_validation/calculate_model_memory_limit.ts similarity index 97% rename from x-pack/test/api_integration/apis/ml/calculate_model_memory_limit.ts rename to x-pack/test/api_integration/apis/ml/job_validation/calculate_model_memory_limit.ts index 59e3dfcca00f9..f17814633ce8f 100644 --- a/x-pack/test/api_integration/apis/ml/calculate_model_memory_limit.ts +++ b/x-pack/test/api_integration/apis/ml/job_validation/calculate_model_memory_limit.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { FtrProviderContext } from '../../ftr_provider_context'; -import { USER } from '../../../functional/services/machine_learning/security_common'; +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { USER } from '../../../../functional/services/machine_learning/security_common'; const COMMON_HEADERS = { 'kbn-xsrf': 'some-xsrf-token', diff --git a/x-pack/test/api_integration/apis/ml/job_validation/index.ts b/x-pack/test/api_integration/apis/ml/job_validation/index.ts new file mode 100644 index 0000000000000..6ca9dcbbe9e5b --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/job_validation/index.ts @@ -0,0 +1,13 @@ +/* + * 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('job validation', function() { + loadTestFile(require.resolve('./bucket_span_estimator')); + loadTestFile(require.resolve('./calculate_model_memory_limit')); + }); +} diff --git a/x-pack/test/api_integration/apis/ml/categorization_field_examples.ts b/x-pack/test/api_integration/apis/ml/jobs/categorization_field_examples.ts similarity index 98% rename from x-pack/test/api_integration/apis/ml/categorization_field_examples.ts rename to x-pack/test/api_integration/apis/ml/jobs/categorization_field_examples.ts index df0153f965942..bcc6c4907100c 100644 --- a/x-pack/test/api_integration/apis/ml/categorization_field_examples.ts +++ b/x-pack/test/api_integration/apis/ml/jobs/categorization_field_examples.ts @@ -6,8 +6,8 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; -import { USER } from '../../../functional/services/machine_learning/security_common'; +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { USER } from '../../../../functional/services/machine_learning/security_common'; const COMMON_HEADERS = { 'kbn-xsrf': 'some-xsrf-token', diff --git a/x-pack/test/api_integration/apis/ml/jobs/index.ts b/x-pack/test/api_integration/apis/ml/jobs/index.ts new file mode 100644 index 0000000000000..70a64f198d6f4 --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/jobs/index.ts @@ -0,0 +1,13 @@ +/* + * 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('jobs', function() { + loadTestFile(require.resolve('./categorization_field_examples')); + loadTestFile(require.resolve('./jobs_summary')); + }); +} diff --git a/x-pack/test/api_integration/apis/ml/jobs/jobs_summary.ts b/x-pack/test/api_integration/apis/ml/jobs/jobs_summary.ts new file mode 100644 index 0000000000000..6a57db1687868 --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/jobs/jobs_summary.ts @@ -0,0 +1,374 @@ +/* + * 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 { USER } from '../../../../functional/services/machine_learning/security_common'; +import { Job } from '../../../../../plugins/ml/common/types/anomaly_detection_jobs'; + +const COMMON_HEADERS = { + 'kbn-xsrf': 'some-xsrf-token', +}; + +const SINGLE_METRIC_JOB_CONFIG: Job = { + job_id: `jobs_summary_fq_single_${Date.now()}`, + description: 'mean(responsetime) on farequote dataset with 15m bucket span', + groups: ['farequote', 'automated', 'single-metric'], + analysis_config: { + bucket_span: '15m', + influencers: [], + detectors: [ + { + function: 'mean', + field_name: 'responsetime', + }, + ], + }, + data_description: { time_field: '@timestamp' }, + analysis_limits: { model_memory_limit: '10mb' }, + model_plot_config: { enabled: true }, +}; + +const MULTI_METRIC_JOB_CONFIG: Job = { + job_id: `jobs_summary_fq_multi_${Date.now()}`, + description: 'mean(responsetime) partition=airline on farequote dataset with 1h bucket span', + groups: ['farequote', 'automated', 'multi-metric'], + analysis_config: { + bucket_span: '1h', + influencers: ['airline'], + detectors: [{ function: 'mean', field_name: 'responsetime', partition_field_name: 'airline' }], + }, + data_description: { time_field: '@timestamp' }, + analysis_limits: { model_memory_limit: '20mb' }, + model_plot_config: { enabled: true }, +}; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertestWithoutAuth'); + const ml = getService('ml'); + + const testSetupJobConfigs = [SINGLE_METRIC_JOB_CONFIG, MULTI_METRIC_JOB_CONFIG]; + + const testDataListNoJobId = [ + { + testTitle: 'as ML Poweruser', + user: USER.ML_POWERUSER, + requestBody: {}, + expected: { + responseCode: 200, + responseBody: [ + { + id: SINGLE_METRIC_JOB_CONFIG.job_id, + description: SINGLE_METRIC_JOB_CONFIG.description, + groups: SINGLE_METRIC_JOB_CONFIG.groups, + processed_record_count: 0, + memory_status: 'ok', + jobState: 'closed', + hasDatafeed: false, + datafeedId: '', + datafeedIndices: [], + datafeedState: '', + isSingleMetricViewerJob: true, + }, + { + id: MULTI_METRIC_JOB_CONFIG.job_id, + description: MULTI_METRIC_JOB_CONFIG.description, + groups: MULTI_METRIC_JOB_CONFIG.groups, + processed_record_count: 0, + memory_status: 'ok', + jobState: 'closed', + hasDatafeed: false, + datafeedId: '', + datafeedIndices: [], + datafeedState: '', + isSingleMetricViewerJob: true, + }, + ], + }, + }, + { + testTitle: 'as ML Viewer', + user: USER.ML_VIEWER, + requestBody: {}, + expected: { + responseCode: 200, + responseBody: [ + { + id: SINGLE_METRIC_JOB_CONFIG.job_id, + description: SINGLE_METRIC_JOB_CONFIG.description, + groups: SINGLE_METRIC_JOB_CONFIG.groups, + processed_record_count: 0, + memory_status: 'ok', + jobState: 'closed', + hasDatafeed: false, + datafeedId: '', + datafeedIndices: [], + datafeedState: '', + isSingleMetricViewerJob: true, + }, + { + id: MULTI_METRIC_JOB_CONFIG.job_id, + description: MULTI_METRIC_JOB_CONFIG.description, + groups: MULTI_METRIC_JOB_CONFIG.groups, + processed_record_count: 0, + memory_status: 'ok', + jobState: 'closed', + hasDatafeed: false, + datafeedId: '', + datafeedIndices: [], + datafeedState: '', + isSingleMetricViewerJob: true, + }, + ], + }, + }, + ]; + + const testDataListWithJobId = [ + { + testTitle: 'as ML Poweruser', + user: USER.ML_POWERUSER, + requestBody: { + jobIds: [SINGLE_METRIC_JOB_CONFIG.job_id], + }, + expected: { + responseCode: 200, + responseBody: [ + { + id: SINGLE_METRIC_JOB_CONFIG.job_id, + description: SINGLE_METRIC_JOB_CONFIG.description, + groups: SINGLE_METRIC_JOB_CONFIG.groups, + processed_record_count: 0, + memory_status: 'ok', + jobState: 'closed', + hasDatafeed: false, + datafeedId: '', + datafeedIndices: [], + datafeedState: '', + isSingleMetricViewerJob: true, + fullJob: { + // Only tests against some of the fields in the fullJob property. + job_id: SINGLE_METRIC_JOB_CONFIG.job_id, + job_type: 'anomaly_detector', + description: SINGLE_METRIC_JOB_CONFIG.description, + groups: SINGLE_METRIC_JOB_CONFIG.groups, + analysis_config: { + bucket_span: '15m', + detectors: [ + { + detector_description: 'mean(responsetime)', + function: 'mean', + field_name: 'responsetime', + detector_index: 0, + }, + ], + influencers: [], + }, + }, + }, + { + id: MULTI_METRIC_JOB_CONFIG.job_id, + description: MULTI_METRIC_JOB_CONFIG.description, + groups: MULTI_METRIC_JOB_CONFIG.groups, + processed_record_count: 0, + memory_status: 'ok', + jobState: 'closed', + hasDatafeed: false, + datafeedId: '', + datafeedIndices: [], + datafeedState: '', + isSingleMetricViewerJob: true, + }, + ], + }, + }, + ]; + + const testDataListNegative = [ + { + testTitle: 'as ML Unauthorized user', + user: USER.ML_UNAUTHORIZED, + requestBody: {}, + // Note that the jobs and datafeeds are loaded async so the actual error message is not deterministic. + expected: { + responseCode: 403, + error: 'Forbidden', + }, + }, + ]; + + async function runJobsSummaryRequest( + user: USER, + requestBody: object, + expectedResponsecode: number + ): Promise { + const { body } = await supertest + .post('/api/ml/jobs/jobs_summary') + .auth(user, ml.securityCommon.getPasswordForUser(user)) + .set(COMMON_HEADERS) + .send(requestBody) + .expect(expectedResponsecode); + + return body; + } + + function compareById(a: { id: string }, b: { id: string }) { + if (a.id < b.id) { + return -1; + } + if (a.id > b.id) { + return 1; + } + return 0; + } + + function getGroups(jobs: Array<{ groups: string[] }>) { + const groupIds: string[] = []; + jobs.forEach(job => { + const groups = job.groups; + groups.forEach(group => { + if (groupIds.indexOf(group) === -1) { + groupIds.push(group); + } + }); + }); + return groupIds.sort(); + } + + describe('jobs_summary', function() { + before(async () => { + await esArchiver.loadIfNeeded('ml/farequote'); + await ml.testResources.setKibanaTimeZoneToUTC(); + }); + + after(async () => { + await ml.api.cleanMlIndices(); + }); + + it('sets up jobs', async () => { + for (const job of testSetupJobConfigs) { + await ml.api.createAnomalyDetectionJob(job); + } + }); + + for (const testData of testDataListNoJobId) { + describe('gets job summary with no job IDs supplied', function() { + it(`${testData.testTitle}`, async () => { + const body = await runJobsSummaryRequest( + testData.user, + testData.requestBody, + testData.expected.responseCode + ); + + // Validate the important parts of the response. + const expectedResponse = testData.expected.responseBody; + + // Validate job count. + expect(body).to.have.length(expectedResponse.length); + + // Validate job IDs. + const expectedRspJobIds = expectedResponse + .map((job: { id: string }) => { + return { id: job.id }; + }) + .sort(compareById); + const actualRspJobIds = body + .map((job: { id: string }) => { + return { id: job.id }; + }) + .sort(compareById); + + expect(actualRspJobIds).to.eql(expectedRspJobIds); + + // Validate created group IDs. + const expectedRspGroupIds = getGroups(expectedResponse); + const actualRspGroupsIds = getGroups(body); + expect(actualRspGroupsIds).to.eql(expectedRspGroupIds); + }); + }); + } + + for (const testData of testDataListWithJobId) { + describe('gets job summary with job ID supplied', function() { + it(`${testData.testTitle}`, async () => { + const body = await runJobsSummaryRequest( + testData.user, + testData.requestBody, + testData.expected.responseCode + ); + + // Validate the important parts of the response. + const expectedResponse = testData.expected.responseBody; + + // Validate job count. + expect(body).to.have.length(expectedResponse.length); + + // Validate job IDs. + const expectedRspJobIds = expectedResponse + .map((job: { id: string }) => { + return { id: job.id }; + }) + .sort(compareById); + const actualRspJobIds = body + .map((job: { id: string }) => { + return { id: job.id }; + }) + .sort(compareById); + + expect(actualRspJobIds).to.eql(expectedRspJobIds); + + // Validate created group IDs. + const expectedRspGroupIds = getGroups(expectedResponse); + const actualRspGroupsIds = getGroups(body); + expect(actualRspGroupsIds).to.eql(expectedRspGroupIds); + + // Validate the response for the specified job IDs contains a fullJob property. + const requestedJobIds = testData.requestBody.jobIds; + for (const job of body) { + if (requestedJobIds.includes(job.id)) { + expect(job).to.have.property('fullJob'); + } else { + expect(job).not.to.have.property('fullJob'); + } + } + + for (const expectedJob of expectedResponse) { + const expectedJobId = expectedJob.id; + const actualJob = body.find((job: { id: string }) => job.id === expectedJobId); + if (expectedJob.fullJob) { + expect(actualJob).to.have.property('fullJob'); + expect(actualJob.fullJob).to.have.property('analysis_config'); + expect(actualJob.fullJob.analysis_config).to.eql(expectedJob.fullJob.analysis_config); + } else { + expect(actualJob).not.to.have.property('fullJob'); + } + } + }); + }); + } + + for (const testData of testDataListNegative) { + describe('rejects request', function() { + it(testData.testTitle, async () => { + const body = await runJobsSummaryRequest( + testData.user, + testData.requestBody, + testData.expected.responseCode + ); + + expect(body) + .to.have.property('error') + .eql(testData.expected.error); + + expect(body).to.have.property('message'); + }); + }); + } + }); +}; diff --git a/x-pack/test/api_integration/apis/ml/get_module.ts b/x-pack/test/api_integration/apis/ml/modules/get_module.ts similarity index 92% rename from x-pack/test/api_integration/apis/ml/get_module.ts rename to x-pack/test/api_integration/apis/ml/modules/get_module.ts index a50d3c0abe430..e19d45999c88e 100644 --- a/x-pack/test/api_integration/apis/ml/get_module.ts +++ b/x-pack/test/api_integration/apis/ml/modules/get_module.ts @@ -6,8 +6,8 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; -import { USER } from '../../../functional/services/machine_learning/security_common'; +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { USER } from '../../../../functional/services/machine_learning/security_common'; const COMMON_HEADERS = { 'kbn-xsrf': 'some-xsrf-token', diff --git a/x-pack/test/api_integration/apis/ml/modules/index.ts b/x-pack/test/api_integration/apis/ml/modules/index.ts new file mode 100644 index 0000000000000..4fdc404c607aa --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/modules/index.ts @@ -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. + */ +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function({ loadTestFile }: FtrProviderContext) { + describe('modules', function() { + loadTestFile(require.resolve('./get_module')); + loadTestFile(require.resolve('./recognize_module')); + loadTestFile(require.resolve('./setup_module')); + }); +} diff --git a/x-pack/test/api_integration/apis/ml/recognize_module.ts b/x-pack/test/api_integration/apis/ml/modules/recognize_module.ts similarity index 93% rename from x-pack/test/api_integration/apis/ml/recognize_module.ts rename to x-pack/test/api_integration/apis/ml/modules/recognize_module.ts index 8e360579c1459..948728189b8bd 100644 --- a/x-pack/test/api_integration/apis/ml/recognize_module.ts +++ b/x-pack/test/api_integration/apis/ml/modules/recognize_module.ts @@ -6,8 +6,8 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; -import { USER } from '../../../functional/services/machine_learning/security_common'; +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { USER } from '../../../../functional/services/machine_learning/security_common'; const COMMON_HEADERS = { 'kbn-xsrf': 'some-xsrf-token', diff --git a/x-pack/test/api_integration/apis/ml/setup_module.ts b/x-pack/test/api_integration/apis/ml/modules/setup_module.ts similarity index 96% rename from x-pack/test/api_integration/apis/ml/setup_module.ts rename to x-pack/test/api_integration/apis/ml/modules/setup_module.ts index e603782b25717..23ddd3b63a2ef 100644 --- a/x-pack/test/api_integration/apis/ml/setup_module.ts +++ b/x-pack/test/api_integration/apis/ml/modules/setup_module.ts @@ -6,10 +6,10 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; -import { JOB_STATE, DATAFEED_STATE } from '../../../../plugins/ml/common/constants/states'; -import { USER } from '../../../functional/services/machine_learning/security_common'; +import { JOB_STATE, DATAFEED_STATE } from '../../../../../plugins/ml/common/constants/states'; +import { USER } from '../../../../functional/services/machine_learning/security_common'; const COMMON_HEADERS = { 'kbn-xsrf': 'some-xsrf-token', From 95ac47d3d26a5f0e3c19669f86c69c82fc9c1614 Mon Sep 17 00:00:00 2001 From: Aleh Zasypkin Date: Thu, 23 Apr 2020 19:15:45 +0200 Subject: [PATCH 34/39] Allow IdP initiated SAML login with session containing expired token. (#59686) --- .../authentication/providers/saml.test.ts | 204 +++++++++--------- .../server/authentication/providers/saml.ts | 12 +- .../server/authentication/tokens.test.ts | 151 +++++++------ .../security/server/authentication/tokens.ts | 20 +- .../apis/security/saml_login.ts | 165 ++++++++------ 5 files changed, 311 insertions(+), 241 deletions(-) diff --git a/x-pack/plugins/security/server/authentication/providers/saml.test.ts b/x-pack/plugins/security/server/authentication/providers/saml.test.ts index a7a43a3031571..ec50ac090f1e7 100644 --- a/x-pack/plugins/security/server/authentication/providers/saml.test.ts +++ b/x-pack/plugins/security/server/authentication/providers/saml.test.ts @@ -315,117 +315,123 @@ describe('SAMLAuthenticationProvider', () => { }); }); - it('redirects to the home page if new SAML Response is for the same user.', async () => { - const request = httpServerMock.createKibanaRequest({ headers: {} }); - const state = { - username: 'user', - accessToken: 'existing-valid-token', - refreshToken: 'existing-valid-refresh-token', - realm: 'test-realm', - }; - const authorization = `Bearer ${state.accessToken}`; - - const user = { username: 'user' }; - const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockResolvedValue(user); - mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); - - mockOptions.client.callAsInternalUser.mockResolvedValue({ - username: 'user', - access_token: 'new-valid-token', - refresh_token: 'new-valid-refresh-token', - }); - - mockOptions.tokens.invalidate.mockResolvedValue(undefined); - - await expect( - provider.login( - request, - { type: SAMLLogin.LoginWithSAMLResponse, samlResponse: 'saml-response-xml' }, - state - ) - ).resolves.toEqual( - AuthenticationResult.redirectTo('/base-path/', { - state: { - username: 'user', - accessToken: 'new-valid-token', - refreshToken: 'new-valid-refresh-token', - realm: 'test-realm', - }, - }) - ); - - expectAuthenticateCall(mockOptions.client, { headers: { authorization } }); + for (const [description, response] of [ + ['session is valid', Promise.resolve({ username: 'user' })], + [ + 'session is is expired', + Promise.reject(ElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error())), + ], + ] as Array<[string, Promise]>) { + it(`redirects to the home page if new SAML Response is for the same user if ${description}.`, async () => { + const request = httpServerMock.createKibanaRequest({ headers: {} }); + const state = { + username: 'user', + accessToken: 'existing-token', + refreshToken: 'existing-refresh-token', + realm: 'test-realm', + }; + const authorization = `Bearer ${state.accessToken}`; - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith( - 'shield.samlAuthenticate', - { - body: { ids: [], content: 'saml-response-xml', realm: 'test-realm' }, - } - ); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.callAsCurrentUser.mockImplementation(() => response); + mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); - expect(mockOptions.tokens.invalidate).toHaveBeenCalledTimes(1); - expect(mockOptions.tokens.invalidate).toHaveBeenCalledWith({ - accessToken: state.accessToken, - refreshToken: state.refreshToken, - }); - }); + mockOptions.client.callAsInternalUser.mockResolvedValue({ + username: 'user', + access_token: 'new-valid-token', + refresh_token: 'new-valid-refresh-token', + }); + + mockOptions.tokens.invalidate.mockResolvedValue(undefined); + + await expect( + provider.login( + request, + { type: SAMLLogin.LoginWithSAMLResponse, samlResponse: 'saml-response-xml' }, + state + ) + ).resolves.toEqual( + AuthenticationResult.redirectTo('/base-path/', { + state: { + username: 'user', + accessToken: 'new-valid-token', + refreshToken: 'new-valid-refresh-token', + realm: 'test-realm', + }, + }) + ); - it('redirects to `overwritten_session` if new SAML Response is for the another user.', async () => { - const request = httpServerMock.createKibanaRequest({ headers: {} }); - const state = { - username: 'user', - accessToken: 'existing-valid-token', - refreshToken: 'existing-valid-refresh-token', - realm: 'test-realm', - }; - const authorization = `Bearer ${state.accessToken}`; + expectAuthenticateCall(mockOptions.client, { headers: { authorization } }); - const existingUser = { username: 'user' }; - const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockResolvedValue(existingUser); - mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); + expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith( + 'shield.samlAuthenticate', + { + body: { ids: [], content: 'saml-response-xml', realm: 'test-realm' }, + } + ); - mockOptions.client.callAsInternalUser.mockResolvedValue({ - username: 'new-user', - access_token: 'new-valid-token', - refresh_token: 'new-valid-refresh-token', + expect(mockOptions.tokens.invalidate).toHaveBeenCalledTimes(1); + expect(mockOptions.tokens.invalidate).toHaveBeenCalledWith({ + accessToken: state.accessToken, + refreshToken: state.refreshToken, + }); }); - mockOptions.tokens.invalidate.mockResolvedValue(undefined); + it(`redirects to \`overwritten_session\` if new SAML Response is for the another user if ${description}.`, async () => { + const request = httpServerMock.createKibanaRequest({ headers: {} }); + const state = { + username: 'user', + accessToken: 'existing-token', + refreshToken: 'existing-refresh-token', + realm: 'test-realm', + }; + const authorization = `Bearer ${state.accessToken}`; - await expect( - provider.login( - request, - { type: SAMLLogin.LoginWithSAMLResponse, samlResponse: 'saml-response-xml' }, - state - ) - ).resolves.toEqual( - AuthenticationResult.redirectTo('/mock-server-basepath/security/overwritten_session', { - state: { - username: 'new-user', - accessToken: 'new-valid-token', - refreshToken: 'new-valid-refresh-token', - realm: 'test-realm', - }, - }) - ); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.callAsCurrentUser.mockImplementation(() => response); + mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); + + mockOptions.client.callAsInternalUser.mockResolvedValue({ + username: 'new-user', + access_token: 'new-valid-token', + refresh_token: 'new-valid-refresh-token', + }); + + mockOptions.tokens.invalidate.mockResolvedValue(undefined); + + await expect( + provider.login( + request, + { type: SAMLLogin.LoginWithSAMLResponse, samlResponse: 'saml-response-xml' }, + state + ) + ).resolves.toEqual( + AuthenticationResult.redirectTo('/mock-server-basepath/security/overwritten_session', { + state: { + username: 'new-user', + accessToken: 'new-valid-token', + refreshToken: 'new-valid-refresh-token', + realm: 'test-realm', + }, + }) + ); - expectAuthenticateCall(mockOptions.client, { headers: { authorization } }); + expectAuthenticateCall(mockOptions.client, { headers: { authorization } }); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith( - 'shield.samlAuthenticate', - { - body: { ids: [], content: 'saml-response-xml', realm: 'test-realm' }, - } - ); + expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith( + 'shield.samlAuthenticate', + { + body: { ids: [], content: 'saml-response-xml', realm: 'test-realm' }, + } + ); - expect(mockOptions.tokens.invalidate).toHaveBeenCalledTimes(1); - expect(mockOptions.tokens.invalidate).toHaveBeenCalledWith({ - accessToken: state.accessToken, - refreshToken: state.refreshToken, + expect(mockOptions.tokens.invalidate).toHaveBeenCalledTimes(1); + expect(mockOptions.tokens.invalidate).toHaveBeenCalledWith({ + accessToken: state.accessToken, + refreshToken: state.refreshToken, + }); }); - }); + } }); describe('User initiated login with captured redirect URL', () => { diff --git a/x-pack/plugins/security/server/authentication/providers/saml.ts b/x-pack/plugins/security/server/authentication/providers/saml.ts index e14d34d1901eb..5c5ec49890901 100644 --- a/x-pack/plugins/security/server/authentication/providers/saml.ts +++ b/x-pack/plugins/security/server/authentication/providers/saml.ts @@ -158,10 +158,14 @@ export class SAMLAuthenticationProvider extends BaseAuthenticationProvider { return await this.loginWithSAMLResponse(request, samlResponse, state); } - if (authenticationResult.succeeded()) { - // If user has been authenticated via session, but request also includes SAML payload - // we should check whether this payload is for the exactly same user and if not - // we'll re-authenticate user and forward to a page with the respective warning. + // If user has been authenticated via session or failed to do so because of expired access token, + // but request also includes SAML payload we should check whether this payload is for the exactly + // same user and if not we'll re-authenticate user and forward to a page with the respective warning. + if ( + authenticationResult.succeeded() || + (authenticationResult.failed() && + Tokens.isAccessTokenExpiredError(authenticationResult.error)) + ) { return await this.loginWithNewSAMLResponse( request, samlResponse, diff --git a/x-pack/plugins/security/server/authentication/tokens.test.ts b/x-pack/plugins/security/server/authentication/tokens.test.ts index 82f29310c04c0..57366183050d7 100644 --- a/x-pack/plugins/security/server/authentication/tokens.test.ts +++ b/x-pack/plugins/security/server/authentication/tokens.test.ts @@ -25,7 +25,7 @@ describe('Tokens', () => { tokens = new Tokens(tokensOptions); }); - it('isAccessTokenExpiredError() returns `true` only if token expired or its document is missing', () => { + it('isAccessTokenExpiredError() returns `true` only if token expired', () => { const nonExpirationErrors = [ {}, new Error(), @@ -91,55 +91,66 @@ describe('Tokens', () => { }); describe('invalidate()', () => { - it('throws if call to delete access token responds with an error', async () => { - const tokenPair = { accessToken: 'foo', refreshToken: 'bar' }; - - const failureReason = new Error('failed to delete token'); - mockClusterClient.callAsInternalUser.mockImplementation((methodName, args: any) => { - if (args && args.body && args.body.token) { - return Promise.reject(failureReason); - } - - return Promise.resolve({ invalidated_tokens: 1 }); + for (const [description, failureReason] of [ + ['an unknown error', new Error('failed to delete token')], + ['a 404 error without body', { statusCode: 404 }], + ] as Array<[string, object]>) { + it(`throws if call to delete access token responds with ${description}`, async () => { + const tokenPair = { accessToken: 'foo', refreshToken: 'bar' }; + + mockClusterClient.callAsInternalUser.mockImplementation((methodName, args: any) => { + if (args && args.body && args.body.token) { + return Promise.reject(failureReason); + } + + return Promise.resolve({ invalidated_tokens: 1 }); + }); + + await expect(tokens.invalidate(tokenPair)).rejects.toBe(failureReason); + + expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(2); + expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( + 'shield.deleteAccessToken', + { + body: { token: tokenPair.accessToken }, + } + ); + expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( + 'shield.deleteAccessToken', + { + body: { refresh_token: tokenPair.refreshToken }, + } + ); }); - await expect(tokens.invalidate(tokenPair)).rejects.toBe(failureReason); - - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(2); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( - 'shield.deleteAccessToken', - { body: { token: tokenPair.accessToken } } - ); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( - 'shield.deleteAccessToken', - { body: { refresh_token: tokenPair.refreshToken } } - ); - }); - - it('throws if call to delete refresh token responds with an error', async () => { - const tokenPair = { accessToken: 'foo', refreshToken: 'bar' }; - - const failureReason = new Error('failed to delete token'); - mockClusterClient.callAsInternalUser.mockImplementation((methodName, args: any) => { - if (args && args.body && args.body.refresh_token) { - return Promise.reject(failureReason); - } - - return Promise.resolve({ invalidated_tokens: 1 }); + it(`throws if call to delete refresh token responds with ${description}`, async () => { + const tokenPair = { accessToken: 'foo', refreshToken: 'bar' }; + + mockClusterClient.callAsInternalUser.mockImplementation((methodName, args: any) => { + if (args && args.body && args.body.refresh_token) { + return Promise.reject(failureReason); + } + + return Promise.resolve({ invalidated_tokens: 1 }); + }); + + await expect(tokens.invalidate(tokenPair)).rejects.toBe(failureReason); + + expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(2); + expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( + 'shield.deleteAccessToken', + { + body: { token: tokenPair.accessToken }, + } + ); + expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( + 'shield.deleteAccessToken', + { + body: { refresh_token: tokenPair.refreshToken }, + } + ); }); - - await expect(tokens.invalidate(tokenPair)).rejects.toBe(failureReason); - - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(2); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( - 'shield.deleteAccessToken', - { body: { token: tokenPair.accessToken } } - ); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( - 'shield.deleteAccessToken', - { body: { refresh_token: tokenPair.refreshToken } } - ); - }); + } it('invalidates all provided tokens', async () => { const tokenPair = { accessToken: 'foo', refreshToken: 'bar' }; @@ -187,23 +198,35 @@ describe('Tokens', () => { ); }); - it('does not fail if none of the tokens were invalidated', async () => { - const tokenPair = { accessToken: 'foo', refreshToken: 'bar' }; - - mockClusterClient.callAsInternalUser.mockResolvedValue({ invalidated_tokens: 0 }); - - await expect(tokens.invalidate(tokenPair)).resolves.toBe(undefined); - - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(2); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( - 'shield.deleteAccessToken', - { body: { token: tokenPair.accessToken } } - ); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( - 'shield.deleteAccessToken', - { body: { refresh_token: tokenPair.refreshToken } } - ); - }); + for (const [description, response] of [ + ['none of the tokens were invalidated', Promise.resolve({ invalidated_tokens: 0 })], + [ + '404 error is returned', + Promise.reject({ statusCode: 404, body: { invalidated_tokens: 0 } }), + ], + ] as Array<[string, Promise]>) { + it(`does not fail if ${description}`, async () => { + const tokenPair = { accessToken: 'foo', refreshToken: 'bar' }; + + mockClusterClient.callAsInternalUser.mockImplementation(() => response); + + await expect(tokens.invalidate(tokenPair)).resolves.toBe(undefined); + + expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(2); + expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( + 'shield.deleteAccessToken', + { + body: { token: tokenPair.accessToken }, + } + ); + expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( + 'shield.deleteAccessToken', + { + body: { refresh_token: tokenPair.refreshToken }, + } + ); + }); + } it('does not fail if more than one token per access or refresh token were invalidated', async () => { const tokenPair = { accessToken: 'foo', refreshToken: 'bar' }; diff --git a/x-pack/plugins/security/server/authentication/tokens.ts b/x-pack/plugins/security/server/authentication/tokens.ts index ea7b5d5a9ff38..9117c9a679a4a 100644 --- a/x-pack/plugins/security/server/authentication/tokens.ts +++ b/x-pack/plugins/security/server/authentication/tokens.ts @@ -103,8 +103,15 @@ export class Tokens { ).invalidated_tokens; } catch (err) { this.logger.debug(`Failed to invalidate refresh token: ${err.message}`); - // We don't re-throw the error here to have a chance to invalidate access token if it's provided. - invalidationError = err; + + // When using already deleted refresh token, Elasticsearch responds with 404 and a body that + // shows that no tokens were invalidated. + if (getErrorStatusCode(err) === 404 && err.body?.invalidated_tokens === 0) { + invalidatedTokensCount = err.body.invalidated_tokens; + } else { + // We don't re-throw the error here to have a chance to invalidate access token if it's provided. + invalidationError = err; + } } if (invalidatedTokensCount === 0) { @@ -128,7 +135,14 @@ export class Tokens { ).invalidated_tokens; } catch (err) { this.logger.debug(`Failed to invalidate access token: ${err.message}`); - invalidationError = err; + + // When using already deleted access token, Elasticsearch responds with 404 and a body that + // shows that no tokens were invalidated. + if (getErrorStatusCode(err) === 404 && err.body?.invalidated_tokens === 0) { + invalidatedTokensCount = err.body.invalidated_tokens; + } else { + invalidationError = err; + } } if (invalidatedTokensCount === 0) { diff --git a/x-pack/test/saml_api_integration/apis/security/saml_login.ts b/x-pack/test/saml_api_integration/apis/security/saml_login.ts index 3b11ef61a1ab2..0b127288e7958 100644 --- a/x-pack/test/saml_api_integration/apis/security/saml_login.ts +++ b/x-pack/test/saml_api_integration/apis/security/saml_login.ts @@ -35,7 +35,7 @@ export default function({ getService }: FtrProviderContext) { }); } - async function checkSessionCookie(sessionCookie: Cookie) { + async function checkSessionCookie(sessionCookie: Cookie, username = 'a@b.c') { expect(sessionCookie.key).to.be('sid'); expect(sessionCookie.value).to.not.be.empty(); expect(sessionCookie.path).to.be('/'); @@ -59,7 +59,7 @@ export default function({ getService }: FtrProviderContext) { 'authentication_provider', ]); - expect(apiResponse.body.username).to.be('a@b.c'); + expect(apiResponse.body.username).to.be(username); } describe('SAML authentication', () => { @@ -668,6 +668,29 @@ export default function({ getService }: FtrProviderContext) { const existingUsername = 'a@b.c'; let existingSessionCookie: Cookie; + const testScenarios: Array<[string, () => Promise]> = [ + // Default scenario when active cookie has an active access token. + ['when access token is valid', async () => {}], + // Scenario when active cookie has an expired access token. Access token expiration is set + // to 15s for API integration tests so we need to wait for 20s to make sure token expires. + ['when access token is expired', async () => await delay(20000)], + // Scenario when active cookie references to access/refresh token pair that were already + // removed from Elasticsearch (to simulate 24h when expired tokens are removed). + [ + 'when access token document is missing', + async () => { + const esResponse = await getService('legacyEs').deleteByQuery({ + index: '.security-tokens', + q: 'doc_type:token', + refresh: true, + }); + expect(esResponse) + .to.have.property('deleted') + .greaterThan(0); + }, + ], + ]; + beforeEach(async () => { const captureURLResponse = await supertest .get('/abc/xyz/handshake?one=two three') @@ -701,76 +724,76 @@ export default function({ getService }: FtrProviderContext) { )!; }); - it('should renew session and redirect to the home page if login is for the same user', async () => { - const samlAuthenticationResponse = await supertest - .post('/api/security/saml/callback') - .set('kbn-xsrf', 'xxx') - .set('Cookie', existingSessionCookie.cookieString()) - .send({ SAMLResponse: await createSAMLResponse({ username: existingUsername }) }) - .expect('location', '/') - .expect(302); - - const newSessionCookie = request.cookie( - samlAuthenticationResponse.headers['set-cookie'][0] - )!; - expect(newSessionCookie.value).to.not.be.empty(); - expect(newSessionCookie.value).to.not.equal(existingSessionCookie.value); - - // Tokens from old cookie are invalidated. - const rejectedResponse = await supertest - .get('/internal/security/me') - .set('kbn-xsrf', 'xxx') - .set('Cookie', existingSessionCookie.cookieString()) - .expect(400); - expect(rejectedResponse.body).to.have.property( - 'message', - 'Both access and refresh tokens are expired.' - ); - - // Only tokens from new session are valid. - const acceptedResponse = await supertest - .get('/internal/security/me') - .set('kbn-xsrf', 'xxx') - .set('Cookie', newSessionCookie.cookieString()) - .expect(200); - expect(acceptedResponse.body).to.have.property('username', existingUsername); - }); - - it('should create a new session and redirect to the `overwritten_session` if login is for another user', async () => { - const newUsername = 'c@d.e'; - const samlAuthenticationResponse = await supertest - .post('/api/security/saml/callback') - .set('kbn-xsrf', 'xxx') - .set('Cookie', existingSessionCookie.cookieString()) - .send({ SAMLResponse: await createSAMLResponse({ username: newUsername }) }) - .expect('location', '/security/overwritten_session') - .expect(302); - - const newSessionCookie = request.cookie( - samlAuthenticationResponse.headers['set-cookie'][0] - )!; - expect(newSessionCookie.value).to.not.be.empty(); - expect(newSessionCookie.value).to.not.equal(existingSessionCookie.value); - - // Tokens from old cookie are invalidated. - const rejectedResponse = await supertest - .get('/internal/security/me') - .set('kbn-xsrf', 'xxx') - .set('Cookie', existingSessionCookie.cookieString()) - .expect(400); - expect(rejectedResponse.body).to.have.property( - 'message', - 'Both access and refresh tokens are expired.' - ); + for (const [description, setup] of testScenarios) { + it(`should renew session and redirect to the home page if login is for the same user ${description}`, async () => { + await setup(); + + const samlAuthenticationResponse = await supertest + .post('/api/security/saml/callback') + .set('kbn-xsrf', 'xxx') + .set('Cookie', existingSessionCookie.cookieString()) + .send({ SAMLResponse: await createSAMLResponse({ username: existingUsername }) }) + .expect(302); + + expect(samlAuthenticationResponse.headers.location).to.be('/'); + + const newSessionCookie = request.cookie( + samlAuthenticationResponse.headers['set-cookie'][0] + )!; + expect(newSessionCookie.value).to.not.be.empty(); + expect(newSessionCookie.value).to.not.equal(existingSessionCookie.value); + + // Tokens from old cookie are invalidated. + const rejectedResponse = await supertest + .get('/internal/security/me') + .set('kbn-xsrf', 'xxx') + .set('Cookie', existingSessionCookie.cookieString()) + .expect(400); + expect(rejectedResponse.body).to.have.property( + 'message', + 'Both access and refresh tokens are expired.' + ); + + // Only tokens from new session are valid. + await checkSessionCookie(newSessionCookie); + }); - // Only tokens from new session are valid. - const acceptedResponse = await supertest - .get('/internal/security/me') - .set('kbn-xsrf', 'xxx') - .set('Cookie', newSessionCookie.cookieString()) - .expect(200); - expect(acceptedResponse.body).to.have.property('username', newUsername); - }); + it(`should create a new session and redirect to the \`overwritten_session\` if login is for another user ${description}`, async () => { + await setup(); + + const newUsername = 'c@d.e'; + const samlAuthenticationResponse = await supertest + .post('/api/security/saml/callback') + .set('kbn-xsrf', 'xxx') + .set('Cookie', existingSessionCookie.cookieString()) + .send({ SAMLResponse: await createSAMLResponse({ username: newUsername }) }) + .expect(302); + + expect(samlAuthenticationResponse.headers.location).to.be( + '/security/overwritten_session' + ); + + const newSessionCookie = request.cookie( + samlAuthenticationResponse.headers['set-cookie'][0] + )!; + expect(newSessionCookie.value).to.not.be.empty(); + expect(newSessionCookie.value).to.not.equal(existingSessionCookie.value); + + // Tokens from old cookie are invalidated. + const rejectedResponse = await supertest + .get('/internal/security/me') + .set('kbn-xsrf', 'xxx') + .set('Cookie', existingSessionCookie.cookieString()) + .expect(400); + expect(rejectedResponse.body).to.have.property( + 'message', + 'Both access and refresh tokens are expired.' + ); + + // Only tokens from new session are valid. + await checkSessionCookie(newSessionCookie, newUsername); + }); + } }); describe('handshake with very long URL path or fragment', () => { From e2baff36b8d3a4792af6f865c8bc1b8100fbce43 Mon Sep 17 00:00:00 2001 From: Luke Elmers Date: Thu, 23 Apr 2020 11:39:38 -0600 Subject: [PATCH 35/39] [data.search.aggs]: Create agg types function for terms agg. (#63541) * Minor cleanup on AggConfigJson interface. * Add types for terms agg params & mapping. * Add terms agg expression function. * Register agg functions with expressions service. * Add unit tests for terms expression function. * Update API docs. * AggConfigJson -> AggConfigSerialized * Add serialize(), enforce serializable params, fix subexpressions in terms agg fn. * Simplify getAggTypesFunctions * it() -> test() * Add help text. * Ensure serialize() is used by agg param type. * Add toExpressionAst to AggConfig. * Add json arg to terms agg fn. * Update docs. * Fix typo which caused functional test failures. * Add AggParam.toExpressionAst so AggConfig.toExpressionAst can return function ast instead of expression ast. * Clean up overlooked items. --- ...ns-data-public.aggconfigoptions.enabled.md | 11 - ...plugins-data-public.aggconfigoptions.id.md | 11 - ...in-plugins-data-public.aggconfigoptions.md | 17 +- ...ins-data-public.aggconfigoptions.params.md | 11 - ...ins-data-public.aggconfigoptions.schema.md | 11 - ...ugins-data-public.aggconfigoptions.type.md | 11 - ...lugins-data-public.aggparamtype.makeagg.md | 2 +- ...plugin-plugins-data-public.aggparamtype.md | 2 +- .../kibana-plugin-plugins-data-public.md | 2 +- src/plugins/data/public/plugin.ts | 1 + src/plugins/data/public/public.api.md | 17 +- .../public/search/aggs/agg_config.test.ts | 218 +++++++++++++++++- .../data/public/search/aggs/agg_config.ts | 80 ++++++- .../data/public/search/aggs/agg_configs.ts | 4 +- .../data/public/search/aggs/agg_type.ts | 10 + .../data/public/search/aggs/agg_types.ts | 5 + .../data/public/search/aggs/buckets/terms.ts | 22 +- .../search/aggs/buckets/terms_fn.test.ts | 164 +++++++++++++ .../public/search/aggs/buckets/terms_fn.ts | 181 +++++++++++++++ .../metrics/lib/parent_pipeline_agg_helper.ts | 12 +- .../lib/sibling_pipeline_agg_helper.ts | 14 +- .../aggs/metrics/percentile_ranks.test.ts | 28 +-- .../search/aggs/metrics/percentiles.test.ts | 7 +- .../public/search/aggs/param_types/agg.ts | 16 +- .../public/search/aggs/param_types/base.ts | 3 + .../aggs/test_helpers/function_wrapper.ts | 49 ++++ .../public/search/aggs/test_helpers/index.ts | 1 + src/plugins/data/public/search/aggs/types.ts | 32 ++- .../expressions/utils/serialize_agg_config.ts | 2 +- .../data/public/search/search_service.test.ts | 4 +- .../data/public/search/search_service.ts | 14 +- 31 files changed, 810 insertions(+), 152 deletions(-) delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.enabled.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.id.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.params.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.schema.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.type.md create mode 100644 src/plugins/data/public/search/aggs/buckets/terms_fn.test.ts create mode 100644 src/plugins/data/public/search/aggs/buckets/terms_fn.ts create mode 100644 src/plugins/data/public/search/aggs/test_helpers/function_wrapper.ts diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.enabled.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.enabled.md deleted file mode 100644 index 2ef8c797f4054..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.enabled.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigOptions](./kibana-plugin-plugins-data-public.aggconfigoptions.md) > [enabled](./kibana-plugin-plugins-data-public.aggconfigoptions.enabled.md) - -## AggConfigOptions.enabled property - -Signature: - -```typescript -enabled?: boolean; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.id.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.id.md deleted file mode 100644 index 8939854ab19ca..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.id.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigOptions](./kibana-plugin-plugins-data-public.aggconfigoptions.md) > [id](./kibana-plugin-plugins-data-public.aggconfigoptions.id.md) - -## AggConfigOptions.id property - -Signature: - -```typescript -id?: string; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.md index b841d9b04d6a7..ff8055b8cf1b1 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.md @@ -2,21 +2,12 @@ [Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigOptions](./kibana-plugin-plugins-data-public.aggconfigoptions.md) -## AggConfigOptions interface +## AggConfigOptions type Signature: ```typescript -export interface AggConfigOptions +export declare type AggConfigOptions = Assign; ``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [enabled](./kibana-plugin-plugins-data-public.aggconfigoptions.enabled.md) | boolean | | -| [id](./kibana-plugin-plugins-data-public.aggconfigoptions.id.md) | string | | -| [params](./kibana-plugin-plugins-data-public.aggconfigoptions.params.md) | Record<string, any> | | -| [schema](./kibana-plugin-plugins-data-public.aggconfigoptions.schema.md) | string | | -| [type](./kibana-plugin-plugins-data-public.aggconfigoptions.type.md) | IAggType | | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.params.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.params.md deleted file mode 100644 index 45219a837cc33..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.params.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigOptions](./kibana-plugin-plugins-data-public.aggconfigoptions.md) > [params](./kibana-plugin-plugins-data-public.aggconfigoptions.params.md) - -## AggConfigOptions.params property - -Signature: - -```typescript -params?: Record; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.schema.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.schema.md deleted file mode 100644 index b2b42eb2e5b4d..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.schema.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigOptions](./kibana-plugin-plugins-data-public.aggconfigoptions.md) > [schema](./kibana-plugin-plugins-data-public.aggconfigoptions.schema.md) - -## AggConfigOptions.schema property - -Signature: - -```typescript -schema?: string; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.type.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.type.md deleted file mode 100644 index 866065ce52ba6..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigoptions.type.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigOptions](./kibana-plugin-plugins-data-public.aggconfigoptions.md) > [type](./kibana-plugin-plugins-data-public.aggconfigoptions.type.md) - -## AggConfigOptions.type property - -Signature: - -```typescript -type: IAggType; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggparamtype.makeagg.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggparamtype.makeagg.md index 43f30d73ca6df..a91db7e7aac8b 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggparamtype.makeagg.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggparamtype.makeagg.md @@ -7,5 +7,5 @@ Signature: ```typescript -makeAgg: (agg: TAggConfig, state?: any) => TAggConfig; +makeAgg: (agg: TAggConfig, state?: AggConfigSerialized) => TAggConfig; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggparamtype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggparamtype.md index b75065da91abd..f9733529a315d 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggparamtype.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggparamtype.md @@ -21,5 +21,5 @@ export declare class AggParamType ex | Property | Modifiers | Type | Description | | --- | --- | --- | --- | | [allowedAggs](./kibana-plugin-plugins-data-public.aggparamtype.allowedaggs.md) | | string[] | | -| [makeAgg](./kibana-plugin-plugins-data-public.aggparamtype.makeagg.md) | | (agg: TAggConfig, state?: any) => TAggConfig | | +| [makeAgg](./kibana-plugin-plugins-data-public.aggparamtype.makeagg.md) | | (agg: TAggConfig, state?: AggConfigSerialized) => TAggConfig | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md index 5ef317a57fb0e..604ac5120922b 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md @@ -49,7 +49,6 @@ | Interface | Description | | --- | --- | -| [AggConfigOptions](./kibana-plugin-plugins-data-public.aggconfigoptions.md) | | | [AggParamOption](./kibana-plugin-plugins-data-public.aggparamoption.md) | | | [DataPublicPluginSetup](./kibana-plugin-plugins-data-public.datapublicpluginsetup.md) | | | [DataPublicPluginStart](./kibana-plugin-plugins-data-public.datapublicpluginstart.md) | | @@ -118,6 +117,7 @@ | Type Alias | Description | | --- | --- | +| [AggConfigOptions](./kibana-plugin-plugins-data-public.aggconfigoptions.md) | | | [AggParam](./kibana-plugin-plugins-data-public.aggparam.md) | | | [CustomFilter](./kibana-plugin-plugins-data-public.customfilter.md) | | | [EsQuerySortValue](./kibana-plugin-plugins-data-public.esquerysortvalue.md) | | diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts index 49ef1000d7993..924fcd6730f93 100644 --- a/src/plugins/data/public/plugin.ts +++ b/src/plugins/data/public/plugin.ts @@ -139,6 +139,7 @@ export class DataPublicPlugin implements Plugin; - // (undocumented) - schema?: string; - // (undocumented) +export type AggConfigOptions = Assign; // Warning: (ae-missing-release-tag) "AggGroupNames" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -112,7 +105,7 @@ export class AggParamType extends Ba // (undocumented) allowedAggs: string[]; // (undocumented) - makeAgg: (agg: TAggConfig, state?: any) => TAggConfig; + makeAgg: (agg: TAggConfig, state?: AggConfigSerialized) => TAggConfig; } // Warning: (ae-missing-release-tag) "AggTypeFieldFilters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/src/plugins/data/public/search/aggs/agg_config.test.ts b/src/plugins/data/public/search/aggs/agg_config.test.ts index 2813e3b9c5373..b5df90313230c 100644 --- a/src/plugins/data/public/search/aggs/agg_config.test.ts +++ b/src/plugins/data/public/search/aggs/agg_config.test.ts @@ -24,18 +24,21 @@ import { AggConfigs, CreateAggConfigParams } from './agg_configs'; import { AggType } from './agg_type'; import { AggTypesRegistryStart } from './agg_types_registry'; import { mockDataServices, mockAggTypesRegistry } from './test_helpers'; +import { MetricAggType } from './metrics/metric_agg_type'; import { Field as IndexPatternField, IndexPattern } from '../../index_patterns'; import { stubIndexPatternWithFields } from '../../../public/stubs'; +import { FieldFormatsStart } from '../../field_formats'; import { fieldFormatsServiceMock } from '../../field_formats/mocks'; describe('AggConfig', () => { let indexPattern: IndexPattern; let typesRegistry: AggTypesRegistryStart; - const fieldFormats = fieldFormatsServiceMock.createStartContract(); + let fieldFormats: FieldFormatsStart; beforeEach(() => { jest.restoreAllMocks(); mockDataServices(); + fieldFormats = fieldFormatsServiceMock.createStartContract(); indexPattern = stubIndexPatternWithFields as IndexPattern; typesRegistry = mockAggTypesRegistry(); }); @@ -325,7 +328,7 @@ describe('AggConfig', () => { }); }); - describe('#toJSON', () => { + describe('#serialize', () => { it('includes the aggs id, params, type and schema', () => { const ac = new AggConfigs(indexPattern, [], { typesRegistry, fieldFormats }); const configStates = { @@ -342,7 +345,7 @@ describe('AggConfig', () => { expect(aggConfig.type).toHaveProperty('name', 'date_histogram'); expect(typeof aggConfig.schema).toBe('string'); - const state = aggConfig.toJSON(); + const state = aggConfig.serialize(); expect(state).toHaveProperty('id', '1'); expect(typeof state.params).toBe('object'); expect(state).toHaveProperty('type', 'date_histogram'); @@ -367,6 +370,201 @@ describe('AggConfig', () => { }); }); + describe('#toExpressionAst', () => { + beforeEach(() => { + fieldFormats.getDefaultInstance = (() => ({ + getConverterFor: (t?: string) => t || identity, + })) as any; + indexPattern.fields.getByName = name => + ({ + format: { + getConverterFor: (t?: string) => t || identity, + }, + } as IndexPatternField); + }); + + it('works with primitive param types', () => { + const ac = new AggConfigs(indexPattern, [], { typesRegistry, fieldFormats }); + const configStates = { + enabled: true, + type: 'terms', + schema: 'segment', + params: { + field: 'machine.os.keyword', + order: 'asc', + }, + }; + const aggConfig = ac.createAggConfig(configStates); + expect(aggConfig.toExpressionAst()).toMatchInlineSnapshot(` + Object { + "arguments": Object { + "enabled": Array [ + true, + ], + "id": Array [ + "1", + ], + "missingBucket": Array [ + false, + ], + "missingBucketLabel": Array [ + "Missing", + ], + "order": Array [ + "asc", + ], + "otherBucket": Array [ + false, + ], + "otherBucketLabel": Array [ + "Other", + ], + "schema": Array [ + "segment", + ], + "size": Array [ + 5, + ], + }, + "function": "aggTerms", + "type": "function", + } + `); + }); + + it('creates a subexpression for params of type "agg"', () => { + const ac = new AggConfigs(indexPattern, [], { typesRegistry, fieldFormats }); + const configStates = { + type: 'terms', + params: { + field: 'machine.os.keyword', + order: 'asc', + orderAgg: { + enabled: true, + type: 'terms', + params: { + field: 'bytes', + order: 'asc', + size: 5, + }, + }, + }, + }; + const aggConfig = ac.createAggConfig(configStates); + const aggArg = aggConfig.toExpressionAst()?.arguments.orderAgg; + expect(aggArg).toMatchInlineSnapshot(` + Array [ + Object { + "chain": Array [ + Object { + "arguments": Object { + "enabled": Array [ + true, + ], + "id": Array [ + "1-orderAgg", + ], + "missingBucket": Array [ + false, + ], + "missingBucketLabel": Array [ + "Missing", + ], + "order": Array [ + "asc", + ], + "otherBucket": Array [ + false, + ], + "otherBucketLabel": Array [ + "Other", + ], + "schema": Array [ + "orderAgg", + ], + "size": Array [ + 5, + ], + }, + "function": "aggTerms", + "type": "function", + }, + ], + "type": "expression", + }, + ] + `); + }); + + it('creates a subexpression for param types other than "agg" which have specified toExpressionAst', () => { + // Overwrite the `ranges` param in the `range` agg with a mock toExpressionAst function + const range: MetricAggType = typesRegistry.get('range'); + range.expressionName = 'aggRange'; + const rangesParam = range.params.find(p => p.name === 'ranges'); + rangesParam!.toExpressionAst = (val: any) => ({ + type: 'function', + function: 'aggRanges', + arguments: { + ranges: ['oh hi there!'], + }, + }); + + const ac = new AggConfigs(indexPattern, [], { typesRegistry, fieldFormats }); + const configStates = { + type: 'range', + params: { + field: 'bytes', + }, + }; + + const aggConfig = ac.createAggConfig(configStates); + const ranges = aggConfig.toExpressionAst()!.arguments.ranges; + expect(ranges).toMatchInlineSnapshot(` + Array [ + Object { + "chain": Array [ + Object { + "arguments": Object { + "ranges": Array [ + "oh hi there!", + ], + }, + "function": "aggRanges", + "type": "function", + }, + ], + "type": "expression", + }, + ] + `); + }); + + it('stringifies any other params which are an object', () => { + const ac = new AggConfigs(indexPattern, [], { typesRegistry, fieldFormats }); + const configStates = { + type: 'terms', + params: { + field: 'machine.os.keyword', + order: 'asc', + json: { foo: 'bar' }, + }, + }; + const aggConfig = ac.createAggConfig(configStates); + const json = aggConfig.toExpressionAst()?.arguments.json; + expect(json).toEqual([JSON.stringify(configStates.params.json)]); + }); + + it(`returns undefined if an expressionName doesn't exist on the agg type`, () => { + const ac = new AggConfigs(indexPattern, [], { typesRegistry, fieldFormats }); + const configStates = { + type: 'unknown type', + params: {}, + }; + const aggConfig = ac.createAggConfig(configStates); + expect(aggConfig.toExpressionAst()).toBe(undefined); + }); + }); + describe('#makeLabel', () => { let aggConfig: AggConfig; @@ -422,6 +620,9 @@ describe('AggConfig', () => { let aggConfig: AggConfig; beforeEach(() => { + fieldFormats.getDefaultInstance = (() => ({ + getConverterFor: (t?: string) => t || identity, + })) as any; indexPattern.fields.getByName = name => ({ format: { @@ -434,11 +635,7 @@ describe('AggConfig', () => { type: 'histogram', schema: 'bucket', params: { - field: { - format: { - getConverterFor: (t?: string) => t || identity, - }, - }, + field: 'bytes', }, }; const ac = new AggConfigs(indexPattern, [configStates], { typesRegistry, fieldFormats }); @@ -446,6 +643,11 @@ describe('AggConfig', () => { }); it("returns the field's formatter", () => { + aggConfig.params.field = { + format: { + getConverterFor: (t?: string) => t || identity, + }, + }; expect(aggConfig.fieldFormatter().toString()).toBe( aggConfig .getField() diff --git a/src/plugins/data/public/search/aggs/agg_config.ts b/src/plugins/data/public/search/aggs/agg_config.ts index 6188849e0e6d4..973c69e3d4f5f 100644 --- a/src/plugins/data/public/search/aggs/agg_config.ts +++ b/src/plugins/data/public/search/aggs/agg_config.ts @@ -19,6 +19,8 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionAstFunction, ExpressionAstArgument } from 'src/plugins/expressions/public'; import { IAggType } from './agg_type'; import { writeParams } from './agg_params'; import { IAggConfigs } from './agg_configs'; @@ -27,11 +29,17 @@ import { ISearchSource } from '../search_source'; import { FieldFormatsContentType, KBN_FIELD_TYPES } from '../../../common'; import { FieldFormatsStart } from '../../field_formats'; -export interface AggConfigOptions { - type: IAggType; +type State = string | number | boolean | null | undefined | SerializableState; + +interface SerializableState { + [key: string]: State | State[]; +} + +export interface AggConfigSerialized { + type: string; enabled?: boolean; id?: string; - params?: Record; + params?: SerializableState; schema?: string; } @@ -39,6 +47,8 @@ export interface AggConfigDependencies { fieldFormats: FieldFormatsStart; } +export type AggConfigOptions = Assign; + /** * @name AggConfig * @@ -257,7 +267,10 @@ export class AggConfig { return configDsl; } - toJSON() { + /** + * @returns Returns a serialized representation of an AggConfig. + */ + serialize(): AggConfigSerialized { const params = this.params; const outParams = _.transform( @@ -281,7 +294,64 @@ export class AggConfig { enabled: this.enabled, type: this.type && this.type.name, schema: this.schema, - params: outParams, + params: outParams as SerializableState, + }; + } + + /** + * @deprecated - Use serialize() instead. + */ + toJSON(): AggConfigSerialized { + return this.serialize(); + } + + /** + * @returns Returns an ExpressionAst representing the function for this agg type. + */ + toExpressionAst(): ExpressionAstFunction | undefined { + const functionName = this.type && this.type.expressionName; + const { type, ...rest } = this.serialize(); + if (!functionName || !rest.params) { + // Return undefined - there is no matching expression function for this agg + return; + } + + // Go through each of the params and convert to an array of expression args. + const params = Object.entries(rest.params).reduce((acc, [key, value]) => { + const deserializedParam = this.getAggParams().find(p => p.name === key); + + if (deserializedParam && deserializedParam.toExpressionAst) { + // If the param provides `toExpressionAst`, we call it with the value + const paramExpressionAst = deserializedParam.toExpressionAst(this.getParam(key)); + if (paramExpressionAst) { + acc[key] = [ + { + type: 'expression', + chain: [paramExpressionAst], + }, + ]; + } + } else if (typeof value === 'object') { + // For object params which don't provide `toExpressionAst`, we stringify + acc[key] = [JSON.stringify(value)]; + } else if (typeof value !== 'undefined') { + // Everything else just gets stored in an array if it is defined + acc[key] = [value]; + } + + return acc; + }, {} as Record); + + return { + type: 'function', + function: functionName, + arguments: { + ...params, + // Expression args which are provided to all functions + id: [this.id], + enabled: [this.enabled], + ...(this.schema ? { schema: [this.schema] } : {}), // schema may be undefined + }, }; } diff --git a/src/plugins/data/public/search/aggs/agg_configs.ts b/src/plugins/data/public/search/aggs/agg_configs.ts index 5ad09f824d3e4..d2151a2c5ed4d 100644 --- a/src/plugins/data/public/search/aggs/agg_configs.ts +++ b/src/plugins/data/public/search/aggs/agg_configs.ts @@ -20,7 +20,7 @@ import _ from 'lodash'; import { Assign } from '@kbn/utility-types'; -import { AggConfig, AggConfigOptions, IAggConfig } from './agg_config'; +import { AggConfig, AggConfigSerialized, IAggConfig } from './agg_config'; import { IAggType } from './agg_type'; import { AggTypesRegistryStart } from './agg_types_registry'; import { AggGroupNames } from './agg_groups'; @@ -51,7 +51,7 @@ export interface AggConfigsOptions { fieldFormats: FieldFormatsStart; } -export type CreateAggConfigParams = Assign; +export type CreateAggConfigParams = Assign; /** * @name AggConfigs diff --git a/src/plugins/data/public/search/aggs/agg_type.ts b/src/plugins/data/public/search/aggs/agg_type.ts index 70c116d560c6f..fb0cb609a08cf 100644 --- a/src/plugins/data/public/search/aggs/agg_type.ts +++ b/src/plugins/data/public/search/aggs/agg_type.ts @@ -39,6 +39,7 @@ export interface AggTypeConfig< createFilter?: (aggConfig: TAggConfig, key: any, params?: any) => any; type?: string; dslName?: string; + expressionName?: string; makeLabel?: ((aggConfig: TAggConfig) => string) | (() => string); ordered?: any; hasNoDsl?: boolean; @@ -88,6 +89,14 @@ export class AggType< * @type {string} */ dslName: string; + /** + * the name of the expression function that this aggType represents. + * TODO: this should probably be a required field. + * + * @property name + * @type {string} + */ + expressionName?: string; /** * the user friendly name that will be shown in the ui for this aggType * @@ -219,6 +228,7 @@ export class AggType< this.name = config.name; this.type = config.type || 'metrics'; this.dslName = config.dslName || config.name; + this.expressionName = config.expressionName; this.title = config.title; this.makeLabel = config.makeLabel || constant(this.name); this.ordered = config.ordered; diff --git a/src/plugins/data/public/search/aggs/agg_types.ts b/src/plugins/data/public/search/aggs/agg_types.ts index 4b154c338d48c..da07f581c9274 100644 --- a/src/plugins/data/public/search/aggs/agg_types.ts +++ b/src/plugins/data/public/search/aggs/agg_types.ts @@ -37,6 +37,7 @@ import { getDerivativeMetricAgg } from './metrics/derivative'; import { getCumulativeSumMetricAgg } from './metrics/cumulative_sum'; import { getMovingAvgMetricAgg } from './metrics/moving_avg'; import { getSerialDiffMetricAgg } from './metrics/serial_diff'; + import { getDateHistogramBucketAgg } from './buckets/date_histogram'; import { getHistogramBucketAgg } from './buckets/histogram'; import { getRangeBucketAgg } from './buckets/range'; @@ -103,3 +104,7 @@ export const getAggTypes = ({ getGeoTitleBucketAgg({ getInternalStartServices }), ], }); + +import { aggTerms } from './buckets/terms_fn'; + +export const getAggTypesFunctions = () => [aggTerms]; diff --git a/src/plugins/data/public/search/aggs/buckets/terms.ts b/src/plugins/data/public/search/aggs/buckets/terms.ts index 698e0dfb1d340..a12a1d7ac2d3d 100644 --- a/src/plugins/data/public/search/aggs/buckets/terms.ts +++ b/src/plugins/data/public/search/aggs/buckets/terms.ts @@ -26,7 +26,7 @@ import { isStringOrNumberType, migrateIncludeExcludeFormat, } from './migrate_include_exclude_format'; -import { IAggConfigs } from '../agg_configs'; +import { AggConfigSerialized, IAggConfigs } from '../types'; import { Adapters } from '../../../../../inspector/public'; import { ISearchSource } from '../../search_source'; @@ -63,10 +63,27 @@ export interface TermsBucketAggDependencies { getInternalStartServices: GetInternalStartServicesFn; } +export interface AggParamsTerms { + field: string; + order: 'asc' | 'desc'; + orderBy: string; + orderAgg?: AggConfigSerialized; + size?: number; + missingBucket?: boolean; + missingBucketLabel?: string; + otherBucket?: boolean; + otherBucketLabel?: string; + // advanced + exclude?: string; + include?: string; + json?: string; +} + export const getTermsBucketAgg = ({ getInternalStartServices }: TermsBucketAggDependencies) => new BucketAggType( { name: BUCKET_TYPES.TERMS, + expressionName: 'aggTerms', title: termsTitle, makeLabel(agg) { const params = agg.params; @@ -154,8 +171,7 @@ export const getTermsBucketAgg = ({ getInternalStartServices }: TermsBucketAggDe type: 'agg', allowedAggs: termsAggFilter, default: null, - makeAgg(termsAgg, state) { - state = state || {}; + makeAgg(termsAgg, state = { type: 'count' }) { state.schema = 'orderAgg'; const orderAgg = termsAgg.aggConfigs.createAggConfig(state, { addToAggConfigs: false, diff --git a/src/plugins/data/public/search/aggs/buckets/terms_fn.test.ts b/src/plugins/data/public/search/aggs/buckets/terms_fn.test.ts new file mode 100644 index 0000000000000..f55f1de796013 --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/terms_fn.test.ts @@ -0,0 +1,164 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggTerms } from './terms_fn'; + +describe('agg_expression_functions', () => { + describe('aggTerms', () => { + const fn = functionWrapper(aggTerms()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + order: 'asc', + orderBy: '1', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "exclude": undefined, + "field": "machine.os.keyword", + "include": undefined, + "json": undefined, + "missingBucket": false, + "missingBucketLabel": "Missing", + "order": "asc", + "orderAgg": undefined, + "orderBy": "1", + "otherBucket": false, + "otherBucketLabel": "Other", + "size": 5, + }, + "schema": undefined, + "type": "terms", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + id: '1', + enabled: false, + schema: 'whatever', + field: 'machine.os.keyword', + order: 'desc', + orderBy: '2', + size: 6, + missingBucket: true, + missingBucketLabel: 'missing', + otherBucket: true, + otherBucketLabel: 'other', + exclude: 'ios', + }); + + expect(actual.value).toMatchInlineSnapshot(` + Object { + "enabled": false, + "id": "1", + "params": Object { + "exclude": "ios", + "field": "machine.os.keyword", + "include": undefined, + "json": undefined, + "missingBucket": true, + "missingBucketLabel": "missing", + "order": "desc", + "orderAgg": undefined, + "orderBy": "2", + "otherBucket": true, + "otherBucketLabel": "other", + "size": 6, + }, + "schema": "whatever", + "type": "terms", + } + `); + }); + + test('handles orderAgg as a subexpression', () => { + const actual = fn({ + field: 'machine.os.keyword', + order: 'asc', + orderBy: '1', + orderAgg: fn({ field: 'name', order: 'asc', orderBy: '1' }), + }); + + expect(actual.value.params).toMatchInlineSnapshot(` + Object { + "exclude": undefined, + "field": "machine.os.keyword", + "include": undefined, + "json": undefined, + "missingBucket": false, + "missingBucketLabel": "Missing", + "order": "asc", + "orderAgg": Object { + "enabled": true, + "id": undefined, + "params": Object { + "exclude": undefined, + "field": "name", + "include": undefined, + "json": undefined, + "missingBucket": false, + "missingBucketLabel": "Missing", + "order": "asc", + "orderAgg": undefined, + "orderBy": "1", + "otherBucket": false, + "otherBucketLabel": "Other", + "size": 5, + }, + "schema": undefined, + "type": "terms", + }, + "orderBy": "1", + "otherBucket": false, + "otherBucketLabel": "Other", + "size": 5, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + order: 'asc', + orderBy: '1', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + expect(() => { + fn({ + field: 'machine.os.keyword', + order: 'asc', + orderBy: '1', + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/public/search/aggs/buckets/terms_fn.ts b/src/plugins/data/public/search/aggs/buckets/terms_fn.ts new file mode 100644 index 0000000000000..7980bfabe79fb --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/terms_fn.ts @@ -0,0 +1,181 @@ +/* + * 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 { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from '../../../../../expressions/public'; +import { AggExpressionType, AggExpressionFunctionArgs } from '../'; + +const aggName = 'terms'; +const fnName = 'aggTerms'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +// Since the orderAgg param is an agg nested in a subexpression, we need to +// overwrite the param type to expect a value of type AggExpressionType. +type Arguments = AggArgs & + Assign< + AggArgs, + { orderAgg?: AggArgs['orderAgg'] extends undefined ? undefined : AggExpressionType } + >; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition; + +export const aggTerms = (): FunctionDefinition => ({ + name: fnName, + help: i18n.translate('data.search.aggs.function.buckets.terms.help', { + defaultMessage: 'Generates a serialized agg config for a terms agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.terms.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.buckets.terms.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.terms.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.buckets.terms.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + order: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.buckets.terms.order.help', { + defaultMessage: 'Order in which to return the results: asc or desc', + }), + }, + orderBy: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.terms.orderBy.help', { + defaultMessage: 'Field to order results by', + }), + }, + orderAgg: { + types: ['agg_type'], + help: i18n.translate('data.search.aggs.buckets.terms.orderAgg.help', { + defaultMessage: 'Agg config to use for ordering results', + }), + }, + size: { + types: ['number'], + default: 5, + help: i18n.translate('data.search.aggs.buckets.terms.size.help', { + defaultMessage: 'Max number of buckets to retrieve', + }), + }, + missingBucket: { + types: ['boolean'], + default: false, + help: i18n.translate('data.search.aggs.buckets.terms.missingBucket.help', { + defaultMessage: 'When set to true, groups together any buckets with missing fields', + }), + }, + missingBucketLabel: { + types: ['string'], + default: i18n.translate('data.search.aggs.buckets.terms.missingBucketLabel', { + defaultMessage: 'Missing', + description: `Default label used in charts when documents are missing a field. + Visible when you create a chart with a terms aggregation and enable "Show missing values"`, + }), + help: i18n.translate('data.search.aggs.buckets.terms.missingBucketLabel.help', { + defaultMessage: 'Default label used in charts when documents are missing a field.', + }), + }, + otherBucket: { + types: ['boolean'], + default: false, + help: i18n.translate('data.search.aggs.buckets.terms.otherBucket.help', { + defaultMessage: 'When set to true, groups together any buckets beyond the allowed size', + }), + }, + otherBucketLabel: { + types: ['string'], + default: i18n.translate('data.search.aggs.buckets.terms.otherBucketLabel', { + defaultMessage: 'Other', + }), + help: i18n.translate('data.search.aggs.buckets.terms.otherBucketLabel.help', { + defaultMessage: 'Default label used in charts for documents in the Other bucket', + }), + }, + exclude: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.terms.exclude.help', { + defaultMessage: 'Specific bucket values to exclude from results', + }), + }, + include: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.terms.include.help', { + defaultMessage: 'Specific bucket values to include in results', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.terms.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + let json; + try { + json = args.json ? JSON.parse(args.json) : undefined; + } catch (e) { + throw new Error('Unable to parse json argument string'); + } + + // Need to spread this object to work around TS bug: + // https://github.com/microsoft/TypeScript/issues/15300#issuecomment-436793742 + const orderAgg = args.orderAgg?.value ? { ...args.orderAgg.value } : undefined; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: aggName, + params: { + ...rest, + orderAgg, + json, + }, + }, + }; + }, +}); diff --git a/src/plugins/data/public/search/aggs/metrics/lib/parent_pipeline_agg_helper.ts b/src/plugins/data/public/search/aggs/metrics/lib/parent_pipeline_agg_helper.ts index 3868d8f1bcd16..947394c97bdcd 100644 --- a/src/plugins/data/public/search/aggs/metrics/lib/parent_pipeline_agg_helper.ts +++ b/src/plugins/data/public/search/aggs/metrics/lib/parent_pipeline_agg_helper.ts @@ -36,14 +36,14 @@ const metricAggFilter = [ '!geo_centroid', ]; -const parentPipelineType = i18n.translate( +export const parentPipelineType = i18n.translate( 'data.search.aggs.metrics.parentPipelineAggregationsSubtypeTitle', { defaultMessage: 'Parent Pipeline Aggregations', } ); -const parentPipelineAggHelper = { +export const parentPipelineAggHelper = { subtype: parentPipelineType, params() { return [ @@ -56,13 +56,9 @@ const parentPipelineAggHelper = { name: 'customMetric', type: 'agg', allowedAggs: metricAggFilter, - makeAgg(termsAgg, state: any) { - state = state || { type: 'count' }; - + makeAgg(termsAgg, state = { type: 'count' }) { const metricAgg = termsAgg.aggConfigs.createAggConfig(state, { addToAggConfigs: false }); - metricAgg.id = termsAgg.id + '-metric'; - return metricAgg; }, modifyAggConfigOnSearchRequestStart: forwardModifyAggConfigOnSearchRequestStart( @@ -89,5 +85,3 @@ const parentPipelineAggHelper = { return subAgg ? subAgg.type.getFormat(subAgg) : new (FieldFormat.from(identity))(); }, }; - -export { parentPipelineAggHelper, parentPipelineType }; diff --git a/src/plugins/data/public/search/aggs/metrics/lib/sibling_pipeline_agg_helper.ts b/src/plugins/data/public/search/aggs/metrics/lib/sibling_pipeline_agg_helper.ts index c1d05a39285b7..cee7841a8c3b9 100644 --- a/src/plugins/data/public/search/aggs/metrics/lib/sibling_pipeline_agg_helper.ts +++ b/src/plugins/data/public/search/aggs/metrics/lib/sibling_pipeline_agg_helper.ts @@ -43,14 +43,14 @@ const metricAggFilter: string[] = [ ]; const bucketAggFilter: string[] = []; -const siblingPipelineType = i18n.translate( +export const siblingPipelineType = i18n.translate( 'data.search.aggs.metrics.siblingPipelineAggregationsSubtypeTitle', { defaultMessage: 'Sibling pipeline aggregations', } ); -const siblingPipelineAggHelper = { +export const siblingPipelineAggHelper = { subtype: siblingPipelineType, params() { return [ @@ -59,11 +59,9 @@ const siblingPipelineAggHelper = { type: 'agg', allowedAggs: bucketAggFilter, default: null, - makeAgg(agg: IMetricAggConfig, state: any) { - state = state || { type: 'date_histogram' }; + makeAgg(agg: IMetricAggConfig, state = { type: 'date_histogram' }) { const orderAgg = agg.aggConfigs.createAggConfig(state, { addToAggConfigs: false }); orderAgg.id = agg.id + '-bucket'; - return orderAgg; }, modifyAggConfigOnSearchRequestStart: forwardModifyAggConfigOnSearchRequestStart( @@ -76,11 +74,9 @@ const siblingPipelineAggHelper = { type: 'agg', allowedAggs: metricAggFilter, default: null, - makeAgg(agg: IMetricAggConfig, state: any) { - state = state || { type: 'count' }; + makeAgg(agg: IMetricAggConfig, state = { type: 'count' }) { const orderAgg = agg.aggConfigs.createAggConfig(state, { addToAggConfigs: false }); orderAgg.id = agg.id + '-metric'; - return orderAgg; }, modifyAggConfigOnSearchRequestStart: forwardModifyAggConfigOnSearchRequestStart( @@ -98,5 +94,3 @@ const siblingPipelineAggHelper = { : new (FieldFormat.from(identity))(); }, }; - -export { siblingPipelineAggHelper, siblingPipelineType }; diff --git a/src/plugins/data/public/search/aggs/metrics/percentile_ranks.test.ts b/src/plugins/data/public/search/aggs/metrics/percentile_ranks.test.ts index d20530a17ca65..7491f15aa3002 100644 --- a/src/plugins/data/public/search/aggs/metrics/percentile_ranks.test.ts +++ b/src/plugins/data/public/search/aggs/metrics/percentile_ranks.test.ts @@ -25,21 +25,28 @@ import { import { AggConfigs, IAggConfigs } from '../agg_configs'; import { mockAggTypesRegistry } from '../test_helpers'; import { METRIC_TYPES } from './metric_agg_types'; +import { FieldFormatsStart } from '../../../field_formats'; import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; import { notificationServiceMock } from '../../../../../../../src/core/public/mocks'; import { InternalStartServices } from '../../../types'; describe('AggTypesMetricsPercentileRanksProvider class', function() { let aggConfigs: IAggConfigs; - const aggTypesDependencies: PercentileRanksMetricAggDependencies = { - getInternalStartServices: () => - (({ - fieldFormats: fieldFormatsServiceMock.createStartContract(), - notifications: notificationServiceMock.createStartContract(), - } as unknown) as InternalStartServices), - }; + let fieldFormats: FieldFormatsStart; + let aggTypesDependencies: PercentileRanksMetricAggDependencies; beforeEach(() => { + fieldFormats = fieldFormatsServiceMock.createStartContract(); + fieldFormats.getDefaultInstance = (() => ({ + convert: (t?: string) => t, + })) as any; + aggTypesDependencies = { + getInternalStartServices: () => + (({ + fieldFormats, + notifications: notificationServiceMock.createStartContract(), + } as unknown) as InternalStartServices), + }; const typesRegistry = mockAggTypesRegistry([getPercentileRanksMetricAgg(aggTypesDependencies)]); const field = { name: 'bytes', @@ -61,12 +68,7 @@ describe('AggTypesMetricsPercentileRanksProvider class', function() { type: METRIC_TYPES.PERCENTILE_RANKS, schema: 'metric', params: { - field: { - displayName: 'bytes', - format: { - convert: jest.fn(x => x), - }, - }, + field: 'bytes', customLabel: 'my custom field label', values: [5000, 10000], }, diff --git a/src/plugins/data/public/search/aggs/metrics/percentiles.test.ts b/src/plugins/data/public/search/aggs/metrics/percentiles.test.ts index 0ac1e8417514c..76382c01bcc10 100644 --- a/src/plugins/data/public/search/aggs/metrics/percentiles.test.ts +++ b/src/plugins/data/public/search/aggs/metrics/percentiles.test.ts @@ -61,12 +61,7 @@ describe('AggTypesMetricsPercentilesProvider class', () => { type: METRIC_TYPES.PERCENTILES, schema: 'metric', params: { - field: { - displayName: 'bytes', - format: { - convert: jest.fn(x => `${x}th`), - }, - }, + field: 'bytes', customLabel: 'prince', percents: [95], }, diff --git a/src/plugins/data/public/search/aggs/param_types/agg.ts b/src/plugins/data/public/search/aggs/param_types/agg.ts index e5b53020c3159..e3f8c7c922170 100644 --- a/src/plugins/data/public/search/aggs/param_types/agg.ts +++ b/src/plugins/data/public/search/aggs/param_types/agg.ts @@ -17,13 +17,13 @@ * under the License. */ -import { AggConfig, IAggConfig } from '../agg_config'; +import { AggConfig, IAggConfig, AggConfigSerialized } from '../agg_config'; import { BaseParamType } from './base'; export class AggParamType extends BaseParamType< TAggConfig > { - makeAgg: (agg: TAggConfig, state?: any) => TAggConfig; + makeAgg: (agg: TAggConfig, state?: AggConfigSerialized) => TAggConfig; allowedAggs: string[] = []; constructor(config: Record) { @@ -42,17 +42,25 @@ export class AggParamType extends Ba } if (!config.serialize) { this.serialize = (agg: TAggConfig) => { - return agg.toJSON(); + return agg.serialize(); }; } if (!config.deserialize) { - this.deserialize = (state: unknown, agg?: TAggConfig): TAggConfig => { + this.deserialize = (state: AggConfigSerialized, agg?: TAggConfig): TAggConfig => { if (!agg) { throw new Error('aggConfig was not provided to AggParamType deserialize function'); } return this.makeAgg(agg, state); }; } + if (!config.toExpressionAst) { + this.toExpressionAst = (agg: TAggConfig) => { + if (!agg || !agg.toExpressionAst) { + throw new Error('aggConfig was not provided to AggParamType toExpressionAst function'); + } + return agg.toExpressionAst(); + }; + } this.makeAgg = config.makeAgg; this.valueType = AggConfig; diff --git a/src/plugins/data/public/search/aggs/param_types/base.ts b/src/plugins/data/public/search/aggs/param_types/base.ts index 2cbc5866e284d..a6f7e5adea043 100644 --- a/src/plugins/data/public/search/aggs/param_types/base.ts +++ b/src/plugins/data/public/search/aggs/param_types/base.ts @@ -17,6 +17,7 @@ * under the License. */ +import { ExpressionAstFunction } from 'src/plugins/expressions/public'; import { IAggConfigs } from '../agg_configs'; import { IAggConfig } from '../agg_config'; import { FetchOptions } from '../../fetch'; @@ -37,6 +38,7 @@ export class BaseParamType { ) => void; serialize: (value: any, aggConfig?: TAggConfig) => any; deserialize: (value: any, aggConfig?: TAggConfig) => any; + toExpressionAst?: (value: any) => ExpressionAstFunction | undefined; options: any[]; valueType?: any; @@ -77,6 +79,7 @@ export class BaseParamType { this.write = config.write || defaultWrite; this.serialize = config.serialize; this.deserialize = config.deserialize; + this.toExpressionAst = config.toExpressionAst; this.options = config.options; this.modifyAggConfigOnSearchRequestStart = config.modifyAggConfigOnSearchRequestStart || function() {}; diff --git a/src/plugins/data/public/search/aggs/test_helpers/function_wrapper.ts b/src/plugins/data/public/search/aggs/test_helpers/function_wrapper.ts new file mode 100644 index 0000000000000..cb0e37c0296d7 --- /dev/null +++ b/src/plugins/data/public/search/aggs/test_helpers/function_wrapper.ts @@ -0,0 +1,49 @@ +/* + * 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 { mapValues } from 'lodash'; +import { + AnyExpressionFunctionDefinition, + ExpressionFunctionDefinition, + ExecutionContext, +} from '../../../../../../plugins/expressions/public'; + +/** + * Takes a function spec and passes in default args, + * overriding with any provided args. + * + * Similar to the test helper used in Expressions & Canvas, + * however in this case we are ignoring the input & execution + * context, as they are not applicable to the agg type + * expression functions. + */ +export const functionWrapper = (spec: T) => { + const defaultArgs = mapValues(spec.args, argSpec => argSpec.default); + return ( + args: T extends ExpressionFunctionDefinition< + infer Name, + infer Input, + infer Arguments, + infer Output, + infer Context + > + ? Arguments + : never + ) => spec.fn(null, { ...defaultArgs, ...args }, {} as ExecutionContext); +}; diff --git a/src/plugins/data/public/search/aggs/test_helpers/index.ts b/src/plugins/data/public/search/aggs/test_helpers/index.ts index 131f921586144..63f8ae0ce5f58 100644 --- a/src/plugins/data/public/search/aggs/test_helpers/index.ts +++ b/src/plugins/data/public/search/aggs/test_helpers/index.ts @@ -17,5 +17,6 @@ * under the License. */ +export { functionWrapper } from './function_wrapper'; export { mockAggTypesRegistry } from './mock_agg_types_registry'; export { mockDataServices } from './mock_data_services'; diff --git a/src/plugins/data/public/search/aggs/types.ts b/src/plugins/data/public/search/aggs/types.ts index 4b2b1620ad1d3..95a7a45013567 100644 --- a/src/plugins/data/public/search/aggs/types.ts +++ b/src/plugins/data/public/search/aggs/types.ts @@ -19,21 +19,23 @@ import { IndexPattern } from '../../index_patterns'; import { + AggConfig, + AggConfigSerialized, + AggConfigs, + AggParamsTerms, AggType, + aggTypeFieldFilters, AggTypesRegistrySetup, AggTypesRegistryStart, - AggConfig, - AggConfigs, CreateAggConfigParams, FieldParamType, getCalculateAutoTimeExpression, MetricAggType, - aggTypeFieldFilters, parentPipelineAggHelper, siblingPipelineAggHelper, } from './'; -export { IAggConfig } from './agg_config'; +export { IAggConfig, AggConfigSerialized } from './agg_config'; export { CreateAggConfigParams, IAggConfigs } from './agg_configs'; export { IAggType } from './agg_type'; export { AggParam, AggParamOption } from './agg_params'; @@ -70,3 +72,25 @@ export interface SearchAggsStart { ) => InstanceType; types: AggTypesRegistryStart; } + +/** @internal */ +export interface AggExpressionType { + type: 'agg_type'; + value: AggConfigSerialized; +} + +/** @internal */ +export type AggExpressionFunctionArgs< + Name extends keyof AggParamsMapping +> = AggParamsMapping[Name] & Pick; + +/** + * A global list of the param interfaces for each agg type. + * For now this is internal, but eventually we will probably + * want to make it public. + * + * @internal + */ +export interface AggParamsMapping { + terms: AggParamsTerms; +} diff --git a/src/plugins/data/public/search/expressions/utils/serialize_agg_config.ts b/src/plugins/data/public/search/expressions/utils/serialize_agg_config.ts index 4ca976d328c91..78b4935077d10 100644 --- a/src/plugins/data/public/search/expressions/utils/serialize_agg_config.ts +++ b/src/plugins/data/public/search/expressions/utils/serialize_agg_config.ts @@ -27,7 +27,7 @@ export const serializeAggConfig = (aggConfig: IAggConfig): KibanaDatatableColumn return { type: aggConfig.type.name, indexPatternId: aggConfig.getIndexPattern().id, - aggConfigParams: aggConfig.toJSON().params, + aggConfigParams: aggConfig.serialize().params, }; }; diff --git a/src/plugins/data/public/search/search_service.test.ts b/src/plugins/data/public/search/search_service.test.ts index 19308dd387d3a..b1f7925bec4bb 100644 --- a/src/plugins/data/public/search/search_service.test.ts +++ b/src/plugins/data/public/search/search_service.test.ts @@ -18,9 +18,10 @@ */ import { coreMock } from '../../../../core/public/mocks'; +import { CoreSetup } from '../../../../core/public'; +import { expressionsPluginMock } from '../../../../plugins/expressions/public/mocks'; import { SearchService } from './search_service'; -import { CoreSetup } from '../../../../core/public'; describe('Search service', () => { let searchService: SearchService; @@ -35,6 +36,7 @@ describe('Search service', () => { it('exposes proper contract', async () => { const setup = searchService.setup(mockCoreSetup, { packageInfo: { version: '8' }, + expressions: expressionsPluginMock.createSetupContract(), } as any); expect(setup).toHaveProperty('registerSearchStrategyProvider'); }); diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts index 3f3266b5fe90f..b59524baa9fa7 100644 --- a/src/plugins/data/public/search/search_service.ts +++ b/src/plugins/data/public/search/search_service.ts @@ -18,6 +18,7 @@ */ import { Plugin, CoreSetup, CoreStart, PackageInfo } from '../../../../core/public'; +import { ExpressionsSetup } from '../../../../plugins/expressions/public'; import { SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider } from './sync_search_strategy'; import { @@ -37,6 +38,7 @@ import { GetInternalStartServicesFn } from '../types'; import { SearchInterceptor } from './search_interceptor'; import { getAggTypes, + getAggTypesFunctions, AggType, AggTypesRegistry, AggConfig, @@ -52,9 +54,10 @@ import { FieldFormatsStart } from '../field_formats'; import { ISearchGeneric } from './i_search'; interface SearchServiceSetupDependencies { + expressions: ExpressionsSetup; + getInternalStartServices: GetInternalStartServicesFn; packageInfo: PackageInfo; query: QuerySetup; - getInternalStartServices: GetInternalStartServicesFn; } interface SearchServiceStartDependencies { @@ -97,22 +100,27 @@ export class SearchService implements Plugin { public setup( core: CoreSetup, - { packageInfo, query, getInternalStartServices }: SearchServiceSetupDependencies + { expressions, packageInfo, query, getInternalStartServices }: SearchServiceSetupDependencies ): ISearchSetup { this.esClient = getEsClient(core.injectedMetadata, core.http, packageInfo); this.registerSearchStrategyProvider(SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider); this.registerSearchStrategyProvider(ES_SEARCH_STRATEGY, esSearchStrategyProvider); const aggTypesSetup = this.aggTypesRegistry.setup(); + + // register each agg type const aggTypes = getAggTypes({ query, uiSettings: core.uiSettings, getInternalStartServices, }); - aggTypes.buckets.forEach(b => aggTypesSetup.registerBucket(b)); aggTypes.metrics.forEach(m => aggTypesSetup.registerMetric(m)); + // register expression functions for each agg type + const aggFunctions = getAggTypesFunctions(); + aggFunctions.forEach(fn => expressions.registerFunction(fn)); + return { aggs: { calculateAutoTimeExpression: getCalculateAutoTimeExpression(core.uiSettings), From a53d53369c091c68f688e36d8ab35e97572687ec Mon Sep 17 00:00:00 2001 From: gchaps <33642766+gchaps@users.noreply.github.com> Date: Thu, 23 Apr 2020 11:11:18 -0700 Subject: [PATCH 36/39] [DOCS] Fixes formatting in alerting doc (#64338) --- docs/user/alerting/pre-configured-connectors.asciidoc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/user/alerting/pre-configured-connectors.asciidoc b/docs/user/alerting/pre-configured-connectors.asciidoc index 3db13acfb423e..4c408da92f579 100644 --- a/docs/user/alerting/pre-configured-connectors.asciidoc +++ b/docs/user/alerting/pre-configured-connectors.asciidoc @@ -20,8 +20,7 @@ action are predefined, including the connector name and ID. The following example shows a valid configuration 2 out-of-the box connector. -[source,console] ------------------------- +```js xpack.actions.preconfigured: - id: 'my-slack1' <1> actionTypeId: .slack <2> @@ -40,7 +39,7 @@ The following example shows a valid configuration 2 out-of-the box connector. secrets: <5> user: elastic password: changeme ------------------------- +``` <1> `id` is the action connector identifier. <2> `actionTypeId` is the action type identifier. From 44f9cbcb60187e69343c207d9bd4443aeb6322fa Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Thu, 23 Apr 2020 15:23:39 -0400 Subject: [PATCH 37/39] Expose ability to check if API Keys are enabled (#63454) * expose ability to check if API Keys are enabled * fix mock * Fix typo in test name * simplify key check * fix privilege check * remove unused variable * address PR feedback Co-authored-by: Elastic Machine --- .../authentication/authentication_service.ts | 11 +- .../public/authentication/index.mock.ts | 1 + .../overwritten_session_app.ts | 2 +- .../overwritten_session_page.tsx | 2 +- .../api_keys/api_keys_api_client.ts | 1 + .../api_keys_grid/api_keys_grid_page.test.tsx | 10 +- .../api_keys_grid/api_keys_grid_page.tsx | 46 +++-- .../plugins/security/public/plugin.test.tsx | 4 +- .../server/authentication/api_keys.test.ts | 76 ++++++++ .../server/authentication/api_keys.ts | 34 ++++ .../server/authentication/index.mock.ts | 1 + .../security/server/authentication/index.ts | 1 + x-pack/plugins/security/server/plugin.test.ts | 1 + .../server/routes/api_keys/enabled.test.ts | 118 +++++++++++++ .../server/routes/api_keys/enabled.ts | 27 +++ .../security/server/routes/api_keys/index.ts | 2 + .../server/routes/api_keys/privileges.test.ts | 167 +++++++++++++----- .../server/routes/api_keys/privileges.ts | 31 ++-- .../api_integration/apis/security/api_keys.ts | 28 +++ .../api_integration/apis/security/index.js | 1 + .../apis/security/security_basic.ts | 1 + .../api_integration/config_security_basic.js | 1 + 22 files changed, 479 insertions(+), 87 deletions(-) create mode 100644 x-pack/plugins/security/server/routes/api_keys/enabled.test.ts create mode 100644 x-pack/plugins/security/server/routes/api_keys/enabled.ts create mode 100644 x-pack/test/api_integration/apis/security/api_keys.ts diff --git a/x-pack/plugins/security/public/authentication/authentication_service.ts b/x-pack/plugins/security/public/authentication/authentication_service.ts index 979f7095cf933..2e73b8cd04482 100644 --- a/x-pack/plugins/security/public/authentication/authentication_service.ts +++ b/x-pack/plugins/security/public/authentication/authentication_service.ts @@ -25,6 +25,11 @@ export interface AuthenticationServiceSetup { * Returns currently authenticated user and throws if current user isn't authenticated. */ getCurrentUser: () => Promise; + + /** + * Determines if API Keys are currently enabled. + */ + areAPIKeysEnabled: () => Promise; } export class AuthenticationService { @@ -37,11 +42,15 @@ export class AuthenticationService { const getCurrentUser = async () => (await http.get('/internal/security/me', { asSystemRequest: true })) as AuthenticatedUser; + const areAPIKeysEnabled = async () => + ((await http.get('/internal/security/api_key/_enabled')) as { apiKeysEnabled: boolean }) + .apiKeysEnabled; + loginApp.create({ application, config, getStartServices, http }); logoutApp.create({ application, http }); loggedOutApp.create({ application, getStartServices, http }); overwrittenSessionApp.create({ application, authc: { getCurrentUser }, getStartServices }); - return { getCurrentUser }; + return { getCurrentUser, areAPIKeysEnabled }; } } diff --git a/x-pack/plugins/security/public/authentication/index.mock.ts b/x-pack/plugins/security/public/authentication/index.mock.ts index c8d77a5b62c6f..dee0a24ab27c2 100644 --- a/x-pack/plugins/security/public/authentication/index.mock.ts +++ b/x-pack/plugins/security/public/authentication/index.mock.ts @@ -9,5 +9,6 @@ import { AuthenticationServiceSetup } from './authentication_service'; export const authenticationMock = { createSetup: (): jest.Mocked => ({ getCurrentUser: jest.fn(), + areAPIKeysEnabled: jest.fn(), }), }; diff --git a/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_app.ts b/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_app.ts index 8e0ee73dfb613..213c26d5287dc 100644 --- a/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_app.ts +++ b/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_app.ts @@ -10,7 +10,7 @@ import { AuthenticationServiceSetup } from '../authentication_service'; interface CreateDeps { application: ApplicationSetup; - authc: AuthenticationServiceSetup; + authc: Pick; getStartServices: StartServicesAccessor; } diff --git a/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_page.tsx b/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_page.tsx index 1093957761d1c..5b77266068ebf 100644 --- a/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_page.tsx +++ b/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_page.tsx @@ -14,7 +14,7 @@ import { AuthenticationStatePage } from '../components'; interface Props { basePath: IBasePath; - authc: AuthenticationServiceSetup; + authc: Pick; } export function OverwrittenSessionPage({ authc, basePath }: Props) { diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.ts b/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.ts index 372b1e56a73c4..a127379d97241 100644 --- a/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.ts +++ b/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.ts @@ -10,6 +10,7 @@ import { ApiKey, ApiKeyToInvalidate } from '../../../common/model'; interface CheckPrivilegesResponse { areApiKeysEnabled: boolean; isAdmin: boolean; + canManage: boolean; } interface InvalidateApiKeysResponse { diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.test.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.test.tsx index ae6ef4aa0fc34..dea04a0eac396 100644 --- a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.test.tsx +++ b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.test.tsx @@ -18,7 +18,6 @@ import { APIKeysGridPage } from './api_keys_grid_page'; import { coreMock } from '../../../../../../../src/core/public/mocks'; import { apiKeysAPIClientMock } from '../index.mock'; -const mock403 = () => ({ body: { statusCode: 403 } }); const mock500 = () => ({ body: { error: 'Internal Server Error', message: '', statusCode: 500 } }); const waitForRender = async ( @@ -48,6 +47,7 @@ describe('APIKeysGridPage', () => { apiClientMock.checkPrivileges.mockResolvedValue({ isAdmin: true, areApiKeysEnabled: true, + canManage: true, }); apiClientMock.getApiKeys.mockResolvedValue({ apiKeys: [ @@ -82,6 +82,7 @@ describe('APIKeysGridPage', () => { it('renders a callout when API keys are not enabled', async () => { apiClientMock.checkPrivileges.mockResolvedValue({ isAdmin: true, + canManage: true, areApiKeysEnabled: false, }); @@ -95,7 +96,11 @@ describe('APIKeysGridPage', () => { }); it('renders permission denied if user does not have required permissions', async () => { - apiClientMock.checkPrivileges.mockRejectedValue(mock403()); + apiClientMock.checkPrivileges.mockResolvedValue({ + canManage: false, + isAdmin: false, + areApiKeysEnabled: true, + }); const wrapper = mountWithIntl(); @@ -152,6 +157,7 @@ describe('APIKeysGridPage', () => { beforeEach(() => { apiClientMock.checkPrivileges.mockResolvedValue({ isAdmin: false, + canManage: true, areApiKeysEnabled: true, }); diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.tsx index 698c0d37dbc64..9db09a34d3c3f 100644 --- a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.tsx +++ b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.tsx @@ -26,7 +26,6 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import moment from 'moment-timezone'; -import _ from 'lodash'; import { NotificationsStart } from 'src/core/public'; import { SectionLoading } from '../../../../../../../src/plugins/es_ui_shared/public'; import { ApiKey, ApiKeyToInvalidate } from '../../../../common/model'; @@ -47,10 +46,10 @@ interface State { isLoadingApp: boolean; isLoadingTable: boolean; isAdmin: boolean; + canManage: boolean; areApiKeysEnabled: boolean; apiKeys: ApiKey[]; selectedItems: ApiKey[]; - permissionDenied: boolean; error: any; } @@ -63,9 +62,9 @@ export class APIKeysGridPage extends Component { isLoadingApp: true, isLoadingTable: false, isAdmin: false, + canManage: false, areApiKeysEnabled: false, apiKeys: [], - permissionDenied: false, selectedItems: [], error: undefined, }; @@ -77,19 +76,15 @@ export class APIKeysGridPage extends Component { public render() { const { - permissionDenied, isLoadingApp, isLoadingTable, areApiKeysEnabled, isAdmin, + canManage, error, apiKeys, } = this.state; - if (permissionDenied) { - return ; - } - if (isLoadingApp) { return ( @@ -103,6 +98,10 @@ export class APIKeysGridPage extends Component { ); } + if (!canManage) { + return ; + } + if (error) { const { body: { error: errorTitle, message, statusCode }, @@ -495,26 +494,25 @@ export class APIKeysGridPage extends Component { private async checkPrivileges() { try { - const { isAdmin, areApiKeysEnabled } = await this.props.apiKeysAPIClient.checkPrivileges(); - this.setState({ isAdmin, areApiKeysEnabled }); + const { + isAdmin, + canManage, + areApiKeysEnabled, + } = await this.props.apiKeysAPIClient.checkPrivileges(); + this.setState({ isAdmin, canManage, areApiKeysEnabled }); - if (areApiKeysEnabled) { - this.initiallyLoadApiKeys(); - } else { - // We're done loading and will just show the "Disabled" error. + if (!canManage || !areApiKeysEnabled) { this.setState({ isLoadingApp: false }); - } - } catch (e) { - if (_.get(e, 'body.statusCode') === 403) { - this.setState({ permissionDenied: true, isLoadingApp: false }); } else { - this.props.notifications.toasts.addDanger( - i18n.translate('xpack.security.management.apiKeys.table.fetchingApiKeysErrorMessage', { - defaultMessage: 'Error checking privileges: {message}', - values: { message: _.get(e, 'body.message', '') }, - }) - ); + this.initiallyLoadApiKeys(); } + } catch (e) { + this.props.notifications.toasts.addDanger( + i18n.translate('xpack.security.management.apiKeys.table.fetchingApiKeysErrorMessage', { + defaultMessage: 'Error checking privileges: {message}', + values: { message: e.body?.message ?? '' }, + }) + ); } } diff --git a/x-pack/plugins/security/public/plugin.test.tsx b/x-pack/plugins/security/public/plugin.test.tsx index 122b26378d22b..7c57c4dd997a2 100644 --- a/x-pack/plugins/security/public/plugin.test.tsx +++ b/x-pack/plugins/security/public/plugin.test.tsx @@ -37,7 +37,7 @@ describe('Security Plugin', () => { ) ).toEqual({ __legacyCompat: { logoutUrl: '/some-base-path/logout', tenant: '/some-base-path' }, - authc: { getCurrentUser: expect.any(Function) }, + authc: { getCurrentUser: expect.any(Function), areAPIKeysEnabled: expect.any(Function) }, license: { isEnabled: expect.any(Function), getFeatures: expect.any(Function), @@ -63,7 +63,7 @@ describe('Security Plugin', () => { expect(setupManagementServiceMock).toHaveBeenCalledTimes(1); expect(setupManagementServiceMock).toHaveBeenCalledWith({ - authc: { getCurrentUser: expect.any(Function) }, + authc: { getCurrentUser: expect.any(Function), areAPIKeysEnabled: expect.any(Function) }, license: { isEnabled: expect.any(Function), getFeatures: expect.any(Function), diff --git a/x-pack/plugins/security/server/authentication/api_keys.test.ts b/x-pack/plugins/security/server/authentication/api_keys.test.ts index 836740d0a547f..9f2a628b575d5 100644 --- a/x-pack/plugins/security/server/authentication/api_keys.test.ts +++ b/x-pack/plugins/security/server/authentication/api_keys.test.ts @@ -40,6 +40,82 @@ describe('API Keys', () => { }); }); + describe('areAPIKeysEnabled()', () => { + it('returns false when security feature is disabled', async () => { + mockLicense.isEnabled.mockReturnValue(false); + + const result = await apiKeys.areAPIKeysEnabled(); + expect(result).toEqual(false); + expect(mockScopedClusterClient.callAsCurrentUser).not.toHaveBeenCalled(); + expect(mockScopedClusterClient.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockClusterClient.callAsInternalUser).not.toHaveBeenCalled(); + }); + + it('returns false when the exception metadata indicates api keys are disabled', async () => { + mockLicense.isEnabled.mockReturnValue(true); + const error = new Error(); + (error as any).body = { + error: { 'disabled.feature': 'api_keys' }, + }; + mockClusterClient.callAsInternalUser.mockRejectedValue(error); + const result = await apiKeys.areAPIKeysEnabled(); + expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(1); + expect(result).toEqual(false); + }); + + it('returns true when the operation completes without error', async () => { + mockLicense.isEnabled.mockReturnValue(true); + mockClusterClient.callAsInternalUser.mockResolvedValue({}); + const result = await apiKeys.areAPIKeysEnabled(); + expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(1); + expect(result).toEqual(true); + }); + + it('throws the original error when exception metadata does not indicate that api keys are disabled', async () => { + mockLicense.isEnabled.mockReturnValue(true); + const error = new Error(); + (error as any).body = { + error: { 'disabled.feature': 'something_else' }, + }; + + mockClusterClient.callAsInternalUser.mockRejectedValue(error); + expect(apiKeys.areAPIKeysEnabled()).rejects.toThrowError(error); + expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(1); + }); + + it('throws the original error when exception metadata does not contain `disabled.feature`', async () => { + mockLicense.isEnabled.mockReturnValue(true); + const error = new Error(); + (error as any).body = {}; + + mockClusterClient.callAsInternalUser.mockRejectedValue(error); + expect(apiKeys.areAPIKeysEnabled()).rejects.toThrowError(error); + expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(1); + }); + + it('throws the original error when exception contains no metadata', async () => { + mockLicense.isEnabled.mockReturnValue(true); + const error = new Error(); + + mockClusterClient.callAsInternalUser.mockRejectedValue(error); + expect(apiKeys.areAPIKeysEnabled()).rejects.toThrowError(error); + expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(1); + }); + + it('calls callCluster with proper parameters', async () => { + mockLicense.isEnabled.mockReturnValue(true); + mockClusterClient.callAsInternalUser.mockResolvedValueOnce({}); + + const result = await apiKeys.areAPIKeysEnabled(); + expect(result).toEqual(true); + expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith('shield.invalidateAPIKey', { + body: { + id: 'kibana-api-key-service-test', + }, + }); + }); + }); + describe('create()', () => { it('returns null when security feature is disabled', async () => { mockLicense.isEnabled.mockReturnValue(false); diff --git a/x-pack/plugins/security/server/authentication/api_keys.ts b/x-pack/plugins/security/server/authentication/api_keys.ts index 9df7219cec334..29ff7e1f69f95 100644 --- a/x-pack/plugins/security/server/authentication/api_keys.ts +++ b/x-pack/plugins/security/server/authentication/api_keys.ts @@ -125,6 +125,35 @@ export class APIKeys { this.license = license; } + /** + * Determines if API Keys are enabled in Elasticsearch. + */ + async areAPIKeysEnabled(): Promise { + if (!this.license.isEnabled()) { + return false; + } + + const id = `kibana-api-key-service-test`; + + this.logger.debug( + `Testing if API Keys are enabled by attempting to invalidate a non-existant key: ${id}` + ); + + try { + await this.clusterClient.callAsInternalUser('shield.invalidateAPIKey', { + body: { + id, + }, + }); + return true; + } catch (e) { + if (this.doesErrorIndicateAPIKeysAreDisabled(e)) { + return false; + } + throw e; + } + } + /** * Tries to create an API key for the current user. * @param request Request instance. @@ -247,6 +276,11 @@ export class APIKeys { return result; } + private doesErrorIndicateAPIKeysAreDisabled(e: Record) { + const disabledFeature = e.body?.error?.['disabled.feature']; + return disabledFeature === 'api_keys'; + } + private getGrantParams(authorizationHeader: HTTPAuthorizationHeader): GrantAPIKeyParams { if (authorizationHeader.scheme.toLowerCase() === 'bearer') { return { diff --git a/x-pack/plugins/security/server/authentication/index.mock.ts b/x-pack/plugins/security/server/authentication/index.mock.ts index 8092c1c81017b..9397a7a42b326 100644 --- a/x-pack/plugins/security/server/authentication/index.mock.ts +++ b/x-pack/plugins/security/server/authentication/index.mock.ts @@ -11,6 +11,7 @@ export const authenticationMock = { login: jest.fn(), logout: jest.fn(), isProviderTypeEnabled: jest.fn(), + areAPIKeysEnabled: jest.fn(), createAPIKey: jest.fn(), getCurrentUser: jest.fn(), grantAPIKeyAsInternalUser: jest.fn(), diff --git a/x-pack/plugins/security/server/authentication/index.ts b/x-pack/plugins/security/server/authentication/index.ts index 5d7b49de68d28..d76a5a533d498 100644 --- a/x-pack/plugins/security/server/authentication/index.ts +++ b/x-pack/plugins/security/server/authentication/index.ts @@ -172,6 +172,7 @@ export async function setupAuthentication({ getSessionInfo: authenticator.getSessionInfo.bind(authenticator), isProviderTypeEnabled: authenticator.isProviderTypeEnabled.bind(authenticator), getCurrentUser, + areAPIKeysEnabled: () => apiKeys.areAPIKeysEnabled(), createAPIKey: (request: KibanaRequest, params: CreateAPIKeyParams) => apiKeys.create(request, params), grantAPIKeyAsInternalUser: (request: KibanaRequest) => apiKeys.grantAsInternalUser(request), diff --git a/x-pack/plugins/security/server/plugin.test.ts b/x-pack/plugins/security/server/plugin.test.ts index 4767f57de764c..3ce0198273af9 100644 --- a/x-pack/plugins/security/server/plugin.test.ts +++ b/x-pack/plugins/security/server/plugin.test.ts @@ -69,6 +69,7 @@ describe('Security Plugin', () => { "registerPrivilegesWithCluster": [Function], }, "authc": Object { + "areAPIKeysEnabled": [Function], "createAPIKey": [Function], "getCurrentUser": [Function], "getSessionInfo": [Function], 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 new file mode 100644 index 0000000000000..3c6dc3c0d7bda --- /dev/null +++ b/x-pack/plugins/security/server/routes/api_keys/enabled.test.ts @@ -0,0 +1,118 @@ +/* + * 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 { kibanaResponseFactory, RequestHandlerContext } from '../../../../../../src/core/server'; +import { LicenseCheck } from '../../../../licensing/server'; + +import { httpServerMock } from '../../../../../../src/core/server/mocks'; +import { routeDefinitionParamsMock } from '../index.mock'; +import Boom from 'boom'; +import { defineEnabledApiKeysRoutes } from './enabled'; +import { APIKeys } from '../../authentication/api_keys'; + +interface TestOptions { + licenseCheckResult?: LicenseCheck; + apiResponse?: () => Promise; + asserts: { statusCode: number; result?: Record }; +} + +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() + ); + + 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(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' } }, + }); + + const error = Boom.notAcceptable('test not acceptable message'); + enabledApiKeysTest('returns error from cluster client', { + apiResponse: async () => { + throw error; + }, + asserts: { statusCode: 406, result: error }, + }); + }); + + describe('success', () => { + enabledApiKeysTest('returns true if API Keys are enabled', { + apiResponse: async () => ({}), + asserts: { + statusCode: 200, + result: { + 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, + }, + }, + }); + }); +}); diff --git a/x-pack/plugins/security/server/routes/api_keys/enabled.ts b/x-pack/plugins/security/server/routes/api_keys/enabled.ts new file mode 100644 index 0000000000000..2f5b8343bcd89 --- /dev/null +++ b/x-pack/plugins/security/server/routes/api_keys/enabled.ts @@ -0,0 +1,27 @@ +/* + * 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 { wrapIntoCustomErrorResponse } from '../../errors'; +import { createLicensedRouteHandler } from '../licensed_route_handler'; +import { RouteDefinitionParams } from '..'; + +export function defineEnabledApiKeysRoutes({ router, authc }: RouteDefinitionParams) { + router.get( + { + path: '/internal/security/api_key/_enabled', + validate: false, + }, + createLicensedRouteHandler(async (context, request, response) => { + try { + const apiKeysEnabled = await authc.areAPIKeysEnabled(); + + return response.ok({ body: { apiKeysEnabled } }); + } catch (error) { + return response.customError(wrapIntoCustomErrorResponse(error)); + } + }) + ); +} diff --git a/x-pack/plugins/security/server/routes/api_keys/index.ts b/x-pack/plugins/security/server/routes/api_keys/index.ts index d75eb1bcbe961..7ac37bbead613 100644 --- a/x-pack/plugins/security/server/routes/api_keys/index.ts +++ b/x-pack/plugins/security/server/routes/api_keys/index.ts @@ -7,9 +7,11 @@ import { defineGetApiKeysRoutes } from './get'; import { defineCheckPrivilegesRoutes } from './privileges'; import { defineInvalidateApiKeysRoutes } from './invalidate'; +import { defineEnabledApiKeysRoutes } from './enabled'; import { RouteDefinitionParams } from '..'; export function defineApiKeysRoutes(params: RouteDefinitionParams) { + defineEnabledApiKeysRoutes(params); defineGetApiKeysRoutes(params); defineCheckPrivilegesRoutes(params); defineInvalidateApiKeysRoutes(params); 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 311d50e9eb169..afb67dc3bbfca 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 @@ -11,25 +11,53 @@ import { kibanaResponseFactory, RequestHandlerContext } from '../../../../../../ import { elasticsearchServiceMock, 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; - apiResponses?: Array<() => Promise>; - asserts: { statusCode: number; result?: Record; apiArguments?: unknown[][] }; + callAsInternalUserResponses?: Array<() => Promise>; + callAsCurrentUserResponses?: Array<() => Promise>; + asserts: { + statusCode: number; + result?: Record; + callAsInternalUserAPIArguments?: unknown[][]; + callAsCurrentUserAPIArguments?: unknown[][]; + }; } describe('Check API keys privileges', () => { const getPrivilegesTest = ( description: string, - { licenseCheckResult = { state: 'valid' }, apiResponses = [], asserts }: TestOptions + { + licenseCheckResult = { state: 'valid' }, + callAsInternalUserResponses = [], + callAsCurrentUserResponses = [], + 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() + ); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); mockRouteDefinitionParams.clusterClient.asScoped.mockReturnValue(mockScopedClusterClient); - for (const apiResponse of apiResponses) { + for (const apiResponse of callAsCurrentUserResponses) { mockScopedClusterClient.callAsCurrentUser.mockImplementationOnce(apiResponse); } + for (const apiResponse of callAsInternalUserResponses) { + mockRouteDefinitionParams.clusterClient.callAsInternalUser.mockImplementationOnce( + apiResponse + ); + } defineCheckPrivilegesRoutes(mockRouteDefinitionParams); const [[, handler]] = mockRouteDefinitionParams.router.get.mock.calls; @@ -48,8 +76,8 @@ describe('Check API keys privileges', () => { expect(response.status).toBe(asserts.statusCode); expect(response.payload).toEqual(asserts.result); - if (Array.isArray(asserts.apiArguments)) { - for (const apiArguments of asserts.apiArguments) { + if (Array.isArray(asserts.callAsCurrentUserAPIArguments)) { + for (const apiArguments of asserts.callAsCurrentUserAPIArguments) { expect(mockRouteDefinitionParams.clusterClient.asScoped).toHaveBeenCalledWith( mockRequest ); @@ -58,6 +86,17 @@ describe('Check API keys privileges', () => { } 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(); + } + expect(mockContext.licensing.license.check).toHaveBeenCalledWith('security', 'basic'); }); }; @@ -70,16 +109,21 @@ describe('Check API keys privileges', () => { const error = Boom.notAcceptable('test not acceptable message'); getPrivilegesTest('returns error from cluster client', { - apiResponses: [ + callAsCurrentUserResponses: [ async () => { throw error; }, - async () => {}, ], + callAsInternalUserResponses: [async () => {}], asserts: { - apiArguments: [ - ['shield.hasPrivileges', { body: { cluster: ['manage_security', 'manage_api_key'] } }], - ['shield.getAPIKeys', { owner: true }], + callAsCurrentUserAPIArguments: [ + [ + 'shield.hasPrivileges', + { body: { cluster: ['manage_security', 'manage_api_key', 'manage_own_api_key'] } }, + ], + ], + callAsInternalUserAPIArguments: [ + ['shield.invalidateAPIKey', { body: { id: expect.any(String) } }], ], statusCode: 406, result: error, @@ -89,14 +133,16 @@ describe('Check API keys privileges', () => { describe('success', () => { getPrivilegesTest('returns areApiKeysEnabled and isAdmin', { - apiResponses: [ + callAsCurrentUserResponses: [ async () => ({ username: 'elastic', has_all_requested: true, - cluster: { manage_api_key: true, manage_security: true }, + cluster: { manage_api_key: true, manage_security: true, manage_own_api_key: false }, index: {}, application: {}, }), + ], + callAsInternalUserResponses: [ async () => ({ api_keys: [ { @@ -112,71 +158,108 @@ describe('Check API keys privileges', () => { }), ], asserts: { - apiArguments: [ - ['shield.getAPIKeys', { owner: true }], - ['shield.hasPrivileges', { body: { cluster: ['manage_security', 'manage_api_key'] } }], + callAsCurrentUserAPIArguments: [ + [ + 'shield.hasPrivileges', + { body: { cluster: ['manage_security', 'manage_api_key', 'manage_own_api_key'] } }, + ], + ], + callAsInternalUserAPIArguments: [ + ['shield.invalidateAPIKey', { body: { id: expect.any(String) } }], ], statusCode: 200, - result: { areApiKeysEnabled: true, isAdmin: true }, + result: { areApiKeysEnabled: true, isAdmin: true, canManage: true }, }, }); getPrivilegesTest( - 'returns areApiKeysEnabled=false when getAPIKeys error message includes "api keys are not enabled"', + 'returns areApiKeysEnabled=false when API Keys are disabled in Elasticsearch', { - apiResponses: [ + callAsCurrentUserResponses: [ async () => ({ username: 'elastic', has_all_requested: true, - cluster: { manage_api_key: true, manage_security: true }, + cluster: { manage_api_key: true, manage_security: true, manage_own_api_key: true }, index: {}, application: {}, }), + ], + callAsInternalUserResponses: [ async () => { - throw Boom.unauthorized('api keys are not enabled'); + const error = new Error(); + (error as any).body = { + error: { + 'disabled.feature': 'api_keys', + }, + }; + throw error; }, ], asserts: { - apiArguments: [ - ['shield.getAPIKeys', { owner: true }], - ['shield.hasPrivileges', { body: { cluster: ['manage_security', 'manage_api_key'] } }], + callAsCurrentUserAPIArguments: [ + [ + 'shield.hasPrivileges', + { body: { cluster: ['manage_security', 'manage_api_key', 'manage_own_api_key'] } }, + ], + ], + callAsInternalUserAPIArguments: [ + ['shield.invalidateAPIKey', { body: { id: expect.any(String) } }], ], statusCode: 200, - result: { areApiKeysEnabled: false, isAdmin: true }, + result: { areApiKeysEnabled: false, isAdmin: true, canManage: true }, }, } ); getPrivilegesTest('returns isAdmin=false when user has insufficient privileges', { - apiResponses: [ + callAsCurrentUserResponses: [ async () => ({ username: 'elastic', has_all_requested: true, - cluster: { manage_api_key: false, manage_security: false }, + cluster: { manage_api_key: false, manage_security: false, manage_own_api_key: false }, index: {}, application: {}, }), - async () => ({ - api_keys: [ - { - id: 'si8If24B1bKsmSLTAhJV', - name: 'my-api-key', - creation: 1574089261632, - expiration: 1574175661632, - invalidated: false, - username: 'elastic', - realm: 'reserved', - }, + ], + callAsInternalUserResponses: [async () => ({})], + asserts: { + callAsCurrentUserAPIArguments: [ + [ + 'shield.hasPrivileges', + { body: { cluster: ['manage_security', 'manage_api_key', 'manage_own_api_key'] } }, ], + ], + callAsInternalUserAPIArguments: [ + ['shield.invalidateAPIKey', { body: { id: expect.any(String) } }], + ], + 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 () => ({})], asserts: { - apiArguments: [ - ['shield.getAPIKeys', { owner: true }], - ['shield.hasPrivileges', { body: { cluster: ['manage_security', 'manage_api_key'] } }], + callAsCurrentUserAPIArguments: [ + [ + 'shield.hasPrivileges', + { body: { cluster: ['manage_security', 'manage_api_key', 'manage_own_api_key'] } }, + ], + ], + callAsInternalUserAPIArguments: [ + ['shield.invalidateAPIKey', { body: { id: expect.any(String) } }], ], statusCode: 200, - result: { areApiKeysEnabled: true, isAdmin: false }, + 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 216d1ef1bf4a4..9cccb96752772 100644 --- a/x-pack/plugins/security/server/routes/api_keys/privileges.ts +++ b/x-pack/plugins/security/server/routes/api_keys/privileges.ts @@ -8,7 +8,11 @@ import { wrapIntoCustomErrorResponse } from '../../errors'; import { createLicensedRouteHandler } from '../licensed_route_handler'; import { RouteDefinitionParams } from '..'; -export function defineCheckPrivilegesRoutes({ router, clusterClient }: RouteDefinitionParams) { +export function defineCheckPrivilegesRoutes({ + router, + clusterClient, + authc, +}: RouteDefinitionParams) { router.get( { path: '/internal/security/api_key/privileges', @@ -20,26 +24,25 @@ export function defineCheckPrivilegesRoutes({ router, clusterClient }: RouteDefi const [ { - cluster: { manage_security: manageSecurity, manage_api_key: manageApiKey }, + cluster: { + manage_security: manageSecurity, + manage_api_key: manageApiKey, + manage_own_api_key: manageOwnApiKey, + }, }, - { areApiKeysEnabled }, + areApiKeysEnabled, ] = await Promise.all([ scopedClusterClient.callAsCurrentUser('shield.hasPrivileges', { - body: { cluster: ['manage_security', 'manage_api_key'] }, + body: { cluster: ['manage_security', 'manage_api_key', 'manage_own_api_key'] }, }), - scopedClusterClient.callAsCurrentUser('shield.getAPIKeys', { owner: true }).then( - // If the API returns a truthy result that means it's enabled. - result => ({ areApiKeysEnabled: !!result }), - // This is a brittle dependency upon message. Tracked by https://github.com/elastic/elasticsearch/issues/47759. - e => - e.message.includes('api keys are not enabled') - ? Promise.resolve({ areApiKeysEnabled: false }) - : Promise.reject(e) - ), + authc.areAPIKeysEnabled(), ]); + const isAdmin = manageSecurity || manageApiKey; + const canManage = manageSecurity || manageApiKey || manageOwnApiKey; + return response.ok({ - body: { areApiKeysEnabled, isAdmin: manageSecurity || manageApiKey }, + body: { areApiKeysEnabled, isAdmin, canManage }, }); } catch (error) { return response.customError(wrapIntoCustomErrorResponse(error)); diff --git a/x-pack/test/api_integration/apis/security/api_keys.ts b/x-pack/test/api_integration/apis/security/api_keys.ts new file mode 100644 index 0000000000000..276a5367a419e --- /dev/null +++ b/x-pack/test/api_integration/apis/security/api_keys.ts @@ -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 expect from '@kbn/expect/expect.js'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + describe('API Keys', () => { + describe('GET /internal/security/api_key/_enabled', () => { + it('should indicate that API Keys are enabled', async () => { + await supertest + .get('/internal/security/api_key/_enabled') + .set('kbn-xsrf', 'xxx') + .send() + .expect(200) + .then((response: Record) => { + const payload = response.body; + expect(payload).to.eql({ apiKeysEnabled: true }); + }); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/security/index.js b/x-pack/test/api_integration/apis/security/index.js index ad1876cb717f1..7bb79a589d522 100644 --- a/x-pack/test/api_integration/apis/security/index.js +++ b/x-pack/test/api_integration/apis/security/index.js @@ -11,6 +11,7 @@ export default function({ loadTestFile }) { // Updates here should be mirrored in `./security_basic.ts` if tests // should also run under a basic license. + loadTestFile(require.resolve('./api_keys')); loadTestFile(require.resolve('./basic_login')); loadTestFile(require.resolve('./builtin_es_privileges')); loadTestFile(require.resolve('./change_password')); diff --git a/x-pack/test/api_integration/apis/security/security_basic.ts b/x-pack/test/api_integration/apis/security/security_basic.ts index dcbdb17724249..3e426f210afa8 100644 --- a/x-pack/test/api_integration/apis/security/security_basic.ts +++ b/x-pack/test/api_integration/apis/security/security_basic.ts @@ -13,6 +13,7 @@ export default function({ loadTestFile }: FtrProviderContext) { // Updates here should be mirrored in `./index.js` if tests // should also run under a trial/platinum license. + loadTestFile(require.resolve('./api_keys')); loadTestFile(require.resolve('./basic_login')); loadTestFile(require.resolve('./builtin_es_privileges')); loadTestFile(require.resolve('./change_password')); diff --git a/x-pack/test/api_integration/config_security_basic.js b/x-pack/test/api_integration/config_security_basic.js index d21bfa4d7031a..4c4b77ee5b080 100644 --- a/x-pack/test/api_integration/config_security_basic.js +++ b/x-pack/test/api_integration/config_security_basic.js @@ -13,6 +13,7 @@ export default async function({ readConfigFile }) { config.esTestCluster.serverArgs = [ 'xpack.license.self_generated.type=basic', 'xpack.security.enabled=true', + 'xpack.security.authc.api_key.enabled=true', ]; config.testFiles = [require.resolve('./apis/security/security_basic')]; return config; From a145aa9d5e965246e048637b63cd23d76b5bff06 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 23 Apr 2020 22:42:22 +0300 Subject: [PATCH 38/39] [SIEM][CASE] Api Integration Tests: Configuration (#63948) * Init * Init get_connectors * Test post_configuration * Test patch_configuration * Rename folder Co-authored-by: Elastic Machine --- .../api/cases/configure/patch_configure.ts | 2 +- .../test/case_api_integration/basic/config.ts | 14 +++ .../basic/tests/configure/get_configure.ts | 55 +++++++++++ .../basic/tests/configure/get_connectors.ts | 27 ++++++ .../basic/tests/configure/patch_configure.ts | 81 ++++++++++++++++ .../basic/tests/configure/post_configure.ts | 62 ++++++++++++ .../case_api_integration/basic/tests/index.ts | 20 ++++ .../case_api_integration/common/config.ts | 94 +++++++++++++++++++ .../common/ftr_provider_context.d.ts | 11 +++ .../case_api_integration/common/lib/utils.ts | 71 ++++++++++++++ .../case_api_integration/common/services.ts | 7 ++ 11 files changed, 443 insertions(+), 1 deletion(-) create mode 100644 x-pack/test/case_api_integration/basic/config.ts create mode 100644 x-pack/test/case_api_integration/basic/tests/configure/get_configure.ts create mode 100644 x-pack/test/case_api_integration/basic/tests/configure/get_connectors.ts create mode 100644 x-pack/test/case_api_integration/basic/tests/configure/patch_configure.ts create mode 100644 x-pack/test/case_api_integration/basic/tests/configure/post_configure.ts create mode 100644 x-pack/test/case_api_integration/basic/tests/index.ts create mode 100644 x-pack/test/case_api_integration/common/config.ts create mode 100644 x-pack/test/case_api_integration/common/ftr_provider_context.d.ts create mode 100644 x-pack/test/case_api_integration/common/lib/utils.ts create mode 100644 x-pack/test/case_api_integration/common/services.ts diff --git a/x-pack/plugins/case/server/routes/api/cases/configure/patch_configure.ts b/x-pack/plugins/case/server/routes/api/cases/configure/patch_configure.ts index 47f7d503e32b8..29df97c5f8476 100644 --- a/x-pack/plugins/case/server/routes/api/cases/configure/patch_configure.ts +++ b/x-pack/plugins/case/server/routes/api/cases/configure/patch_configure.ts @@ -39,7 +39,7 @@ export function initPatchCaseConfigure({ caseConfigureService, caseService, rout if (myCaseConfigure.saved_objects.length === 0) { throw Boom.conflict( - 'You can not patch this configuration since you did not created first with a post' + 'You can not patch this configuration since you did not created first with a post.' ); } diff --git a/x-pack/test/case_api_integration/basic/config.ts b/x-pack/test/case_api_integration/basic/config.ts new file mode 100644 index 0000000000000..f9c248ec3d56f --- /dev/null +++ b/x-pack/test/case_api_integration/basic/config.ts @@ -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. + */ + +import { createTestConfig } from '../common/config'; + +// eslint-disable-next-line import/no-default-export +export default createTestConfig('basic', { + disabledPlugins: [], + license: 'basic', + ssl: true, +}); diff --git a/x-pack/test/case_api_integration/basic/tests/configure/get_configure.ts b/x-pack/test/case_api_integration/basic/tests/configure/get_configure.ts new file mode 100644 index 0000000000000..a9fc2706a6ba2 --- /dev/null +++ b/x-pack/test/case_api_integration/basic/tests/configure/get_configure.ts @@ -0,0 +1,55 @@ +/* + * 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 '../../../common/ftr_provider_context'; + +import { CASE_CONFIGURE_URL } from '../../../../../plugins/case/common/constants'; +import { + getConfiguration, + removeServerGeneratedPropertiesFromConfigure, + getConfigurationOutput, + deleteConfiguration, +} from '../../../common/lib/utils'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext): void => { + const supertest = getService('supertest'); + const es = getService('legacyEs'); + + describe('get_configure', () => { + afterEach(async () => { + await deleteConfiguration(es); + }); + + it('should return an empty find body correctly if no configuration is loaded', async () => { + const { body } = await supertest + .get(CASE_CONFIGURE_URL) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + expect(body).to.eql({}); + }); + + it('should return a configuration', async () => { + await supertest + .post(CASE_CONFIGURE_URL) + .set('kbn-xsrf', 'true') + .send(getConfiguration()) + .expect(200); + + const { body } = await supertest + .get(CASE_CONFIGURE_URL) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + const data = removeServerGeneratedPropertiesFromConfigure(body); + expect(data).to.eql(getConfigurationOutput()); + }); + }); +}; diff --git a/x-pack/test/case_api_integration/basic/tests/configure/get_connectors.ts b/x-pack/test/case_api_integration/basic/tests/configure/get_connectors.ts new file mode 100644 index 0000000000000..836c76d500034 --- /dev/null +++ b/x-pack/test/case_api_integration/basic/tests/configure/get_connectors.ts @@ -0,0 +1,27 @@ +/* + * 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 '../../../common/ftr_provider_context'; + +import { CASE_CONFIGURE_CONNECTORS_URL } from '../../../../../plugins/case/common/constants'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext): void => { + const supertest = getService('supertest'); + + describe('get_connectors', () => { + it('should return an empty find body correctly if no connectors are loaded', async () => { + const { body } = await supertest + .get(`${CASE_CONFIGURE_CONNECTORS_URL}/_find`) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + expect(body).to.eql([]); + }); + }); +}; diff --git a/x-pack/test/case_api_integration/basic/tests/configure/patch_configure.ts b/x-pack/test/case_api_integration/basic/tests/configure/patch_configure.ts new file mode 100644 index 0000000000000..d66baa2a2eee2 --- /dev/null +++ b/x-pack/test/case_api_integration/basic/tests/configure/patch_configure.ts @@ -0,0 +1,81 @@ +/* + * 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 '../../../common/ftr_provider_context'; + +import { CASE_CONFIGURE_URL } from '../../../../../plugins/case/common/constants'; +import { + getConfiguration, + removeServerGeneratedPropertiesFromConfigure, + getConfigurationOutput, + deleteConfiguration, +} from '../../../common/lib/utils'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext): void => { + const supertest = getService('supertest'); + const es = getService('legacyEs'); + + describe('post_configure', () => { + afterEach(async () => { + await deleteConfiguration(es); + }); + + it('should patch a configuration', async () => { + const res = await supertest + .post(CASE_CONFIGURE_URL) + .set('kbn-xsrf', 'true') + .send(getConfiguration()) + .expect(200); + + const { body } = await supertest + .patch(CASE_CONFIGURE_URL) + .set('kbn-xsrf', 'true') + .send({ closure_type: 'close-by-pushing', version: res.body.version }) + .expect(200); + + const data = removeServerGeneratedPropertiesFromConfigure(body); + expect(data).to.eql({ ...getConfigurationOutput(true), closure_type: 'close-by-pushing' }); + }); + + it('should handle patch request when there is no configuration', async () => { + const { body } = await supertest + .patch(CASE_CONFIGURE_URL) + .set('kbn-xsrf', 'true') + .send({ closure_type: 'close-by-pushing', version: 'no-version' }) + .expect(409); + + expect(body).to.eql({ + error: 'Conflict', + message: + 'You can not patch this configuration since you did not created first with a post.', + statusCode: 409, + }); + }); + + it('should handle patch request when versions are different', async () => { + await supertest + .post(CASE_CONFIGURE_URL) + .set('kbn-xsrf', 'true') + .send(getConfiguration()) + .expect(200); + + const { body } = await supertest + .patch(CASE_CONFIGURE_URL) + .set('kbn-xsrf', 'true') + .send({ closure_type: 'close-by-pushing', version: 'no-version' }) + .expect(409); + + expect(body).to.eql({ + error: 'Conflict', + message: + 'This configuration has been updated. Please refresh before saving additional updates.', + statusCode: 409, + }); + }); + }); +}; diff --git a/x-pack/test/case_api_integration/basic/tests/configure/post_configure.ts b/x-pack/test/case_api_integration/basic/tests/configure/post_configure.ts new file mode 100644 index 0000000000000..c2284492e5b77 --- /dev/null +++ b/x-pack/test/case_api_integration/basic/tests/configure/post_configure.ts @@ -0,0 +1,62 @@ +/* + * 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 '../../../common/ftr_provider_context'; + +import { CASE_CONFIGURE_URL } from '../../../../../plugins/case/common/constants'; +import { + getConfiguration, + removeServerGeneratedPropertiesFromConfigure, + getConfigurationOutput, + deleteConfiguration, +} from '../../../common/lib/utils'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext): void => { + const supertest = getService('supertest'); + const es = getService('legacyEs'); + + describe('post_configure', () => { + afterEach(async () => { + await deleteConfiguration(es); + }); + + it('should create a configuration', async () => { + const { body } = await supertest + .post(CASE_CONFIGURE_URL) + .set('kbn-xsrf', 'true') + .send(getConfiguration()) + .expect(200); + + const data = removeServerGeneratedPropertiesFromConfigure(body); + expect(data).to.eql(getConfigurationOutput()); + }); + + it('should keep only the latest configuration', async () => { + await supertest + .post(CASE_CONFIGURE_URL) + .set('kbn-xsrf', 'true') + .send(getConfiguration('connector-2')) + .expect(200); + + await supertest + .post(CASE_CONFIGURE_URL) + .set('kbn-xsrf', 'true') + .send(getConfiguration()) + .expect(200); + + const { body } = await supertest + .get(CASE_CONFIGURE_URL) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + const data = removeServerGeneratedPropertiesFromConfigure(body); + expect(data).to.eql(getConfigurationOutput()); + }); + }); +}; diff --git a/x-pack/test/case_api_integration/basic/tests/index.ts b/x-pack/test/case_api_integration/basic/tests/index.ts new file mode 100644 index 0000000000000..efd5369c019d8 --- /dev/null +++ b/x-pack/test/case_api_integration/basic/tests/index.ts @@ -0,0 +1,20 @@ +/* + * 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 '../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default ({ loadTestFile }: FtrProviderContext): void => { + describe('case api basic', function() { + // Fastest ciGroup for the moment. + this.tags('ciGroup2'); + + loadTestFile(require.resolve('./configure/get_configure')); + loadTestFile(require.resolve('./configure/post_configure')); + loadTestFile(require.resolve('./configure/patch_configure')); + loadTestFile(require.resolve('./configure/get_connectors')); + }); +}; diff --git a/x-pack/test/case_api_integration/common/config.ts b/x-pack/test/case_api_integration/common/config.ts new file mode 100644 index 0000000000000..862705ab9610b --- /dev/null +++ b/x-pack/test/case_api_integration/common/config.ts @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import path from 'path'; + +import { CA_CERT_PATH } from '@kbn/dev-utils'; +import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; + +import { services } from './services'; + +interface CreateTestConfigOptions { + license: string; + disabledPlugins?: string[]; + ssl?: boolean; +} + +// test.not-enabled is specifically not enabled +const enabledActionTypes = [ + '.email', + '.index', + '.pagerduty', + '.server-log', + '.servicenow', + '.slack', + '.webhook', + 'test.authorization', + 'test.failing', + 'test.index-record', + 'test.noop', + 'test.rate-limit', +]; + +export function createTestConfig(name: string, options: CreateTestConfigOptions) { + const { license = 'trial', disabledPlugins = [], ssl = false } = options; + + return async ({ readConfigFile }: FtrConfigProviderContext) => { + const xPackApiIntegrationTestsConfig = await readConfigFile( + require.resolve('../../api_integration/config.js') + ); + + const servers = { + ...xPackApiIntegrationTestsConfig.get('servers'), + elasticsearch: { + ...xPackApiIntegrationTestsConfig.get('servers.elasticsearch'), + protocol: ssl ? 'https' : 'http', + }, + }; + + return { + testFiles: [require.resolve(`../${name}/tests/`)], + servers, + services, + junit: { + reportName: 'X-Pack Case API Integration Tests', + }, + esArchiver: xPackApiIntegrationTestsConfig.get('esArchiver'), + esTestCluster: { + ...xPackApiIntegrationTestsConfig.get('esTestCluster'), + license, + ssl, + serverArgs: [ + `xpack.license.self_generated.type=${license}`, + `xpack.security.enabled=${!disabledPlugins.includes('security') && + ['trial', 'basic'].includes(license)}`, + ], + }, + kbnTestServer: { + ...xPackApiIntegrationTestsConfig.get('kbnTestServer'), + serverArgs: [ + ...xPackApiIntegrationTestsConfig.get('kbnTestServer.serverArgs'), + `--xpack.actions.whitelistedHosts=${JSON.stringify([ + 'localhost', + 'some.non.existent.com', + ])}`, + `--xpack.actions.enabledActionTypes=${JSON.stringify(enabledActionTypes)}`, + '--xpack.alerting.enabled=true', + '--xpack.eventLog.logEntries=true', + ...disabledPlugins.map(key => `--xpack.${key}.enabled=false`), + `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'alerts')}`, + `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'actions')}`, + ...(ssl + ? [ + `--elasticsearch.hosts=${servers.elasticsearch.protocol}://${servers.elasticsearch.hostname}:${servers.elasticsearch.port}`, + `--elasticsearch.ssl.certificateAuthorities=${CA_CERT_PATH}`, + ] + : []), + ], + }, + }; + }; +} diff --git a/x-pack/test/case_api_integration/common/ftr_provider_context.d.ts b/x-pack/test/case_api_integration/common/ftr_provider_context.d.ts new file mode 100644 index 0000000000000..e3add3748f56d --- /dev/null +++ b/x-pack/test/case_api_integration/common/ftr_provider_context.d.ts @@ -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. + */ + +import { GenericFtrProviderContext } from '@kbn/test/types/ftr'; + +import { services } from './services'; + +export type FtrProviderContext = GenericFtrProviderContext; diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts new file mode 100644 index 0000000000000..6d0db69309b90 --- /dev/null +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -0,0 +1,71 @@ +/* + * 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 { CasesConfigureRequest, CasesConfigureResponse } from '../../../../plugins/case/common/api'; + +export const getConfiguration = (connector_id: string = 'connector-1'): CasesConfigureRequest => { + return { + connector_id, + connector_name: 'Connector 1', + closure_type: 'close-by-user', + }; +}; + +export const getConfigurationOutput = (update = false): Partial => { + return { + ...getConfiguration(), + created_by: { email: null, full_name: null, username: 'elastic' }, + updated_by: update ? { email: null, full_name: null, username: 'elastic' } : null, + }; +}; + +export const removeServerGeneratedPropertiesFromConfigure = ( + config: Partial +): Partial => { + const { created_at, updated_at, version, ...rest } = config; + return rest; +}; + +export const deleteConfiguration = async (es: any): Promise => { + await es.deleteByQuery({ + index: '.kibana', + q: 'type:cases-configure', + waitForCompletion: true, + refresh: 'wait_for', + body: {}, + }); +}; + +export const getConnector = () => ({ + name: 'ServiceNow Connector', + actionTypeId: '.servicenow', + secrets: { + username: 'admin', + password: 'admin', + }, + config: { + apiUrl: 'localhost', + casesConfiguration: { + mapping: [ + { + source: 'title', + target: 'short_description', + actionType: 'overwrite', + }, + { + source: 'description', + target: 'description', + actionType: 'overwrite', + }, + { + source: 'comments', + target: 'comments', + actionType: 'append', + }, + ], + }, + }, +}); diff --git a/x-pack/test/case_api_integration/common/services.ts b/x-pack/test/case_api_integration/common/services.ts new file mode 100644 index 0000000000000..a927a31469bab --- /dev/null +++ b/x-pack/test/case_api_integration/common/services.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 { services } from '../../api_integration/services'; From 74a07b46d87ede2ed7a61311cbb3b392e4aad805 Mon Sep 17 00:00:00 2001 From: Kaarina Tungseth Date: Thu, 23 Apr 2020 15:00:40 -0500 Subject: [PATCH 39/39] [DOCS] Added images to automating report generation (#64333) * [DOCS] Added images to automating report generation * Fixes image file name --- docs/images/report-automate-csv.png | Bin 0 -> 54061 bytes docs/images/report-automate-pdf.png | Bin 0 -> 52835 bytes .../automating-report-generation.asciidoc | 42 +++++++++++------- 3 files changed, 26 insertions(+), 16 deletions(-) create mode 100644 docs/images/report-automate-csv.png create mode 100644 docs/images/report-automate-pdf.png diff --git a/docs/images/report-automate-csv.png b/docs/images/report-automate-csv.png new file mode 100644 index 0000000000000000000000000000000000000000..fba77821ae29f708ec545470684600033dfadc97 GIT binary patch literal 54061 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 y&yN7Oe*5_R@+)nhvOoWY>k5UyDpM literal 0 HcmV?d00001 diff --git a/docs/images/report-automate-pdf.png b/docs/images/report-automate-pdf.png new file mode 100644 index 0000000000000000000000000000000000000000..f96eebe6fe02d5cf3572378e47b8f17ecbe9773b GIT binary patch literal 52835 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⪼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 u)7z